您的位置:首页 > 其它

NKOI 3744 智力游戏

2016-08-27 19:55 232 查看

P3744智力游戏

问题描述

小A在玩一个简单的智力游戏,这个游戏是这样的:      

在一个4*4的矩阵中分别有4个1,4个2,4个3和4个4。      

每一步小A可以把同一行的4个数往左移或者往右移一步或者把同一列的4个数字往上移或者往下移一步(1,2,3,4往左移后是2,3,4,1)。    

小A现在想知道最少几步移动可以使得矩阵的每行上的4个数字都一样或者每列上的4个数字都一样。但是小A又不想走太多步,他只要知道最少步数是否少于等于5步,是的话输出准确的步数,否则输出-1。

输入格式

一个4*4的矩阵

输出格式

一个整数,表示最优步数。无解输出-1

样例输入 1

1 2 3 4

1 2 3 4

1 2 3 4

2 3 4 1

样例输出 1

1

样例输入 2

4 1 1 1

1 2 2 2

2 3 3 3

3 4 4 4

样例输出 2

1

本题要用到迭代加深dfs+启发式搜索,重点在于估价函数的设计

我们设定估价函数: F(i)=G(i)+H(i)   G(i)记录到达i棋盘状态所需步数 H(i)=min{ 所有行最少所需操作的次数总和,所有列最少所需操作的次数总和 }

这里的最少操作次数是对于单个一行/列来讲的,即这一行/列最少保证到达目标状态的操作次数

我们讨论将下面棋盘转换成合法目标棋盘需要的操作数:

1 1 3 2

2 4 4 4       

3 3 1 2

1 2 3 4

水平方向:第1行至少2次,第2行至少1次,第3行至少2次,第4行至少3次,总次数至少8次

垂直方向:第1列至少2次,第2列至少3次,第3列至少2次,第4列至少2次,总次数至少9次

1 2 3 4

1 2 3 4

1 2 3 4

2 3 4 1

如上样例数据所示,水平方向总次数为3+3+3+3=12,垂直方向总次数为1+1+1+1=4 看起来调整垂直方向更合算。于是,我们将第四行右移一位,发现1次操作达到的最终目的。 而我们用上面方法估算出的次数至少是4。 矛盾的原因是,在理想情况下,我们通过一次移动,可以使得4个数字都到达合适的位置上。

于是,我们修改估价函数:

H(i)=(min{ 所有行最少所需操作的次数总和,所有列最少所需操作的次数总和 }+3)/4

这里+3即为向上取整
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int s[10][10],depth=1;
int geth(){
int temp1=0,temp2=0,i,j;
int cnt[10];
for(i=1;i<=4;i++){
int maxx=0;
memset(cnt,0,sizeof(cnt));
for(j=1;j<=4;j++){
cnt[s[i][j]]++;
maxx=max(cnt[s[i][j]],maxx);
}
temp1+=4-maxx;
}
for(j=1;j<=4;j++){
int maxx=0;
memset(cnt,0,sizeof(cnt));
for(i=1;i<=4;i++){
cnt[s[i][j]]++;
maxx=max(cnt[s[i][j]],maxx);
}
temp2+=4-maxx;
}
return (min(temp1,temp2)+3)/4;
}
void move_right(int x){
int i,y=s[x][4];
for(i=4;i>1;i--)s[x][i]=s[x][i-1];
s[x][1]=y;
}
void move_left(int x){
int i,y=s[x][1];
for(i=1;i<=3;i++)s[x][i]=s[x][i+1];
s[x][4]=y;
}
void move_up(int x){
int i,y=s[1][x];
for(i=1;i<=3;i++)s[i][x]=s[i+1][x];
s[4][x]=y;
}
void move_down(int x){
int i,y=s[4][x];
for(i=4;i>1;i--)s[i][x]=s[i-1][x];
s[1][x]=y;
}
bool dfs(int k){
int h=geth();
if(k+h>depth)return 0;
if(h==0)return 1;
int i;
for(i=1;i<=4;i++){
move_left(i);
if(dfs(k+1))return 1;
move_right(i);

move_right(i);
if(dfs(k+1))return 1;
move_left(i);

move_up(i);
if(dfs(k+1))return 1;
move_down(i);

move_down(i);
if(dfs(k+1))return 1;
move_up(i);
}
return 0;
}
int main(){
int i,j;
for(i=1;i<=4;i++)
for(j=1;j<=4;j++)cin>>s[i][j];
if(!geth()){
puts("0");
return 0;
}
for(depth=1;depth<=5;depth++)
if(dfs(0))break;
printf("%d",depth<=5?depth:-1);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: