「Poetize5」水叮当的舞步
Description
水叮当得到了一块五颜六色的格子形地毯作为生日礼物,更加特别的是,地毯上格子的颜色还能随着踩踏而改变。
为了讨好她的偶像虹猫,水叮当决定在地毯上跳一支轻盈的舞来卖萌~~~
地毯上的格子有N行N列,每个格子用一个0~5之间的数字代表它的颜色。
水叮当可以随意选择一个0~5之间的颜色,然后轻轻地跳动一步,左上角的格子所在的联通块里的所有格子就会变成她选择的那种颜色。这里连通定义为:两个格子有公共边,并且颜色相同。
由于水叮当是施展轻功来跳舞的,为了不消耗过多的真气,她想知道最少要多少步才能把所有格子的颜色变成一样的。
题解:
我来当搬运工。。。
类型:IDA* (迭代加深启发式搜索)
方法一:
枚举每次选取了哪种颜色,然后找出左上角的格子所在的联通块,改变颜色。
为了避免来回改变、搜索深度过大,采用迭代加深的dfs限制搜索步数。
迭代加深也就是,依次限制搜索深度为0、1、2、3……进行搜索,搜索过程中发现深度超过限制就马上退出。只要搜索成功就找到了答案,也可以立即退出。
期望得分:0~10分。
方法二:
加入一个小剪枝:如果改变颜色后,左上角格子所在的联通块大小没有改变,可以剪枝。这样可以避免来回往复地搜索。
期望得分:10~20分。
方法三:
采用IDA*算法,设计估价函数。可以发现如果当前矩阵中除了左上角的联通块之外,共有M种颜色,那么还需要的步数不小于M。因此如果当前搜索深度+估价函数的值>深度限制,可以回溯。
期望得分:50~70分。
方法四:
我们可以发现,每次寻找左上角的格子所在的联通块耗费的时间常数巨大。因此我们在这里寻求突破。
我们引入一个N*N的v数组。左上角的格子所在的联通块里的格子标记为1。左上角联通块周围一圈格子标记为2,其它格子标记为0。如果某次选择了颜色c,我们只需要找出标记为2并且颜色为c的格子,向四周扩展,并相应地修改v标记,就可以不断扩大标记为1的区域,最终如果所有格子标记都是1,那么显然找到了答案。
代码:
1 #include<cstdio> 2 3 #include<cstdlib> 4 5 #include<cmath> 6 7 #include<cstring> 8 9 #include<algorithm> 10 11 #include<iostream> 12 13 #include<vector> 14 15 #include<map> 16 17 #include<set> 18 19 #include<queue> 20 21 #include<string> 22 23 #define inf 1000000000 24 25 #define maxn 500+100 26 27 #define maxm 500+100 28 29 #define eps 1e-10 30 31 #define ll long long 32 33 #define pa pair<int,int> 34 35 #define for0(i,n) for(int i=0;i<=(n);i++) 36 37 #define for1(i,n) for(int i=1;i<=(n);i++) 38 39 #define for2(i,x,y) for(int i=(x);i<=(y);i++) 40 41 #define for3(i,x,y) for(int i=(x);i>=(y);i--) 42 43 #define mod 1000000007 44 45 using namespace std; 46 47 inline int read() 48 49 { 50 51 int x=0,f=1;char ch=getchar(); 52 53 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 54 55 while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();} 56 57 return x*f; 58 59 } 60 const int dx[4]={-1,0,0,1},dy[4]={0,-1,1,0}; 61 int a[9][9],v[9][9],f[6],n,ID; 62 inline int left() 63 { 64 int tmp=0; 65 memset(f,0,sizeof(f)); 66 for1(i,n)for1(j,n) 67 if(!f[a[i][j]]&&v[i][j]!=1){f[a[i][j]]=1;tmp++;} 68 return tmp; 69 } 70 inline void dfs(int x,int y,int c) 71 { 72 v[x][y]=1; 73 for0(i,3) 74 { 75 int xx=x+dx[i],yy=y+dy[i]; 76 if(xx<1||xx>n||yy<1||yy>n||v[xx][yy]==1)continue; 77 v[xx][yy]=2; 78 if(a[xx][yy]==c)dfs(xx,yy,c); 79 } 80 } 81 inline int fill(int c) 82 { 83 int tmp=0; 84 for1(i,n)for1(j,n) 85 if(a[i][j]==c&&v[i][j]==2) 86 { 87 tmp++; 88 dfs(i,j,c); 89 } 90 return tmp; 91 } 92 inline bool IDA(int dep) 93 { 94 int g=left(); 95 if(dep+g>ID)return 0; 96 if(!g)return 1; 97 int rec[9][9]; 98 for0(i,5) 99 { 100 memcpy(rec,v,sizeof(v)); 101 if(fill(i)&&IDA(dep+1))return 1; 102 memcpy(v,rec,sizeof(v)); 103 } 104 return 0; 105 } 106 107 int main() 108 109 { 110 111 freopen("input.txt","r",stdin); 112 113 freopen("output.txt","w",stdout); 114 115 while(cin>>n&&n) 116 { 117 memset(a,0,sizeof(a)); 118 memset(v,0,sizeof(v)); 119 for1(i,n)for1(j,n)a[i][j]=read(); 120 dfs(1,1,a[1][1]); 121 for(ID=0;;ID++) 122 if(IDA(0))break; 123 printf("%d\n",ID); 124 } 125 126 return 0; 127 128 }