水叮当的舞步
题目描述:
水叮当得到了一块五颜六色的格子形地毯作为生日礼物,更加特别的是,地毯上格子的颜色还能随着踩踏而改变。
为了讨好她的偶像虹猫,水叮当决定在地毯上跳一支轻盈的舞来卖萌~~~
地毯上的格子有N行N列,每个格子用一个0~5之间的数字代表它的颜色。
水叮当可以随意选择一个0~5之间的颜色,然后轻轻地跳动一步,左上角的格子所在的联通块里的所有格子就会变成她选择的那种颜色。这里连通定义为:两个格子有公共边,并且颜色相同。
由于水叮当是施展轻功来跳舞的,为了不消耗过多的真气,她想知道最少要多少步才能把所有格子的颜色变成一样的。
输入描述:
每个测试点包含多组数据。
每组数据的第一行是一个整数N,表示地摊上的格子有N行N列。
接下来一个N*N的矩阵,矩阵中的每个数都在0~5之间,描述了每个格子的颜色。
N=0代表输入的结束。
输出描述:
对于每组数据,输出一个整数,表示最少步数。
样例输入:
2
0 0
0 0
3
0 1 2
1 1 2
2 2 1
0
样例输出:
0
3
数据范围及提示:
对于30%的数据,N<=5
对于50%的数据,N<=6
对于70%的数据,N<=7
对于100%的数据,N<=8,每个测试点不多于20组数据。
第二组样例解释:
0 1 2 |1 1 2|2 2 2|1 1 1|
1 1 2 |1 1 2|2 2 2|1 1 1|
2 2 1 |2 2 1|2 2 1|1 1 1|
思路:
迭代搜索+A*搜索
迭代加深的搜索思想:
每次进行深搜限制搜索步数,若是搜索实际步骤和当前答案深度估计值之和大于了限制步数,就直接跳出搜索过程,用这样的方法来加快搜索速度。
A*的搜索思想:
给每一步搜索加一个答案深度的估计值,并与当前使用的步数结合,来判断是否需要走该步(简单来说,就是判断搜索的方向)。一个优质的估价函数可以极大程度的加快答案的计算速度。
解题的方法很简单,每次读入数据后重新建造一个优化图:该图形式和读入的图相同,不过是用1标记与左上角点在同一联通块(同一种颜色)的点,用2标记在该联通块外边界上的点,其余点用0来标记。这样可以大幅度减少寻找联通块的时间和更新的时间。
然后进行限制深度的迭代加深搜索,枚举深度进入搜索:
每次对当前状态进行搜索,先用A*对该点进行估价,估价值为剩下的颜色数,该估价值与当前步数相加若是大于了深度限制则返回false。而当估价值为0时,即返回true,说明找到了答案。
在每次搜索中,枚举当前选择的颜色,并对联通块进行涂色并扩展,如果扩展后并没有增加它的大小,则说明这次涂色是不必要的,就枚举下一个颜色。
#include<iostream>
#include<cstring>
using namespace std;
int n,ans;//ans是迭代搜索时的限制,也是最终的答案
int map[10][10],flag[9][9];
bool hash[10];//估价函数中用来判重
int xx[5]={0,1,-1,0,0},
yy[5]={0,0,0,1,-1};
int compute()//估价函数,获得估价值
{
int tmp=0;
memset(hash,0,sizeof(hash));
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(!hash[map[i][j]]&&flag[i][j]!=1)
{
tmp++;
hash[map[i][j]]=true;
}
return tmp;
}
void dfs(int x,int y,int col)//对联通块进行扩展,col标记当前染的颜色
{
flag[x][y]=1;
for(int i=1;i<=4;i++)
{
int fx=x+xx[i],fy=y+yy[i];
if(fx>=1&&fx<=n&&fy>=1&&fy<=n&&flag[fx][fy]!=1)
{
flag[fx][fy]=2;
if(map[fx][fy]==col)//如果发现该点颜色与要染的颜色相同,就以它重新扩展边界
dfs(fx,fy,col);
}
}
}
int judge(int col)//该次染色对联通块大小有无影响的判断函数
{
int tmp=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(map[i][j]==col&&flag[i][j]==2)
{
tmp++;
dfs(i,j,col);
}
return tmp;
}
bool a_star(int deep)//A*搜索
{
int g=compute();
if(g+deep>ans) return 0;
if(!g) return 1;
int rec[9][9];
for(int i=0;i<=5;i++)//枚举颜色,进行染色
{
memcpy(rec,flag,sizeof(flag));//利用memcpy复制一个备份图 rec
if(judge(i)&&a_star(deep+1))//如果此次染色有利于扩大联通块并且染色后能得到答案,就返回真
return 1;
memcpy(flag,rec,sizeof(flag));//用备份下来的rec还原flag
}
return 0;//以上条件都不满足,返回假
}
int main()
{
while(cin>>n&&n!=0)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>map[i][j];
memset(flag,0,sizeof(flag));
dfs(1,1,map[1][1]);
for(ans=0;ans<=n*n;ans++)//迭代搜索
if(a_star(0)) break;//找到答案,跳出循环
cout<<ans<<endl;//输出答案
}
return 0;
}