NKOJ 2440 数字消除游戏
时间限制 : - MS 空间限制 : 165536 KB
评测说明 : 时限1000ms
问题描述
在一个n*n的方形棋盘上玩消除游戏,棋盘上布满了数字。
每一步,玩家可以任选一个数字x,用它填充坐标为(1,1)格子所在连通区域,该区域的数字都会变成x。(如果两个数字相同且相邻,我们称这两个数字连通。相邻是上下左右四方向)。
当整个棋盘的数字都相同时,就可以将整个棋盘上的数字消除掉,游戏结束。
问,最少需要几次操作就能消除所有数字。
输入格式
有若干组测试数据(不超过20组),对于每组测试数据:
第一行,一个整数n,表示棋盘的尺寸。
接下来一个n*n的数字矩阵,表示游戏的初始局面。
当输入的n==0时,输入结束。
输出格式
对于每组测试数据,输出一行,一个整数,表示最少需要的操作数。
样例输入
2
1 3
3 3
4
5 5 2 0
5 5 2 0
2 2 2 0
0 0 0 2
0
样例输出
1
3
提示
样例说明,对于第二组数据:
第1步 选数字2去填充,得到:
2 2 2 0
2 2 2 0
2 2 2 0
0 0 0 2
第2步 选数字0去填充,得到:
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 2
第3步 选数字2去填充,得到:
2 2 2 2
2 2 2 2
2 2 2 2
2 2 2 2
对于100%的数据,n<=8,
初始棋盘中最多有6种不同的数字
思路:
IDA*,h()表示除左上角连通块外的数字中,不同的数字共有多少种。
剪枝1:当当前步数+h()>d时可回溯。
剪枝2:改变数字后,若连通块没改变,回溯
优化:每次从左上角开始修改太费时,多加一个数组,将已经成为连通块的部分标为1,连通块外围标为2,其余为0,每次更改只用检查标2的位置是否存在于当前讨论数字相同的,有便更新连通块。
由于初始化时需要标一次连通块,搜索时也需要标连通块,所以使用递归的方式标,不用宽搜
#include<cstdio>
#include<iostream>
#include<map>
#include<cstring>
using namespace std;
typedef int int_[9][9];
struct fy
{
int a,b;
fy(int a,int b) {this->a=a,this->b=b;}
};
int_ a,v,b;
const int x[]={0,0,1,-1},y[]={1,-1,0,0};
int n,tot=0;
//.........................................................
int va;
void mark_(int x1,int y1)
{
v[x1][y1]=1;
int xx,yy;
for(int k=0;k<4;k++)
{
xx=x1+x[k],yy=y1+y[k];
if(b[xx][yy]&&v[xx][yy]!=1)
{
if(a[xx][yy]==va) mark_(xx,yy);
else v[xx][yy]=2;
}
}
}
//.........................................................
bool mar[8];
int h()
{
memset(mar,0,sizeof(mar));
int ans=0;
for(int i=1,j;i<=n;i++)
for(j=1;j<=n;j++)
{
if(v[i][j]!=1&&!mar[a[i][j]])
{
mar[a[i][j]]=true;
ans++;
}
}
return ans;
}
//.........................................................
int pa(int c)
{
int xx,yy;
bool flag=false;
for(int i=1,j,k;i<=n;i++)
for(j=1;j<=n;j++)
{
if(v[i][j]==2&&a[i][j]==c)
{
flag=true;
va=c,mark_(i,j);
}
}
return flag;
}
//.........................................................
int d;
bool ida(int now)
{
int hh=h();
if(now+hh>d) return false;
if(hh==0) return true;
int_ cp;
for(int i=1;i<=tot;i++)
{
memcpy(cp,v,sizeof(v));
if(!pa(i)) continue;
if(ida(now+1)) return true;
memcpy(v,cp,sizeof(v));
}
return false;
}
//.........................................................
int main()
{
while(true)
{
scanf("%d",&n);
if(n==0) return 0;
map<int,int> ma;
memset(b,0,sizeof(b));
memset(v,0,sizeof(v));
tot=0;
for(int i=1,j;i<=n;i++)
for(j=1;j<=n;j++)
{
b[i][j]=1;
scanf("%d",&a[i][j]);
a[i][j]= ma.find(a[i][j])==ma.end() ? ma[a[i][j]]=++tot : ma[a[i][j]];
}
v[1][1]=1;
va=a[1][1],mark_(1,1);
for(d=0;;d++)
{
if(ida(0))
{
printf("%d\n",d);
break;
}
}
}
}