P1312 [NOIP2011 提高组] Mayan 游戏
考察:dfs + 剪枝
思路:
搜索顺序是枚举每一个有颜色的格子左右移动的操作,时间复杂度是O(352*5).需要剪枝:
- 搜索顺序剪枝,这个应该没有
- 最优性剪枝.因为我们需要的是字典序最小的序列,也就是列坐标越小越好.可以先枚举列再枚举行,这样如果返回1就一定是最小的.
- 还有一个最优性剪枝:因为操作要列坐标尽可能小,所以某物体左移尽量换成该物体右移.
- 可行性剪枝:如果当前有颜色只剩下1 或 2个,立即return
还有一个十分恶心的操作就是移动更新地图,本蒟蒻想半天不知道怎么回溯,结果是memcpy....
更新地图基本照抄Y总代码,有个很好的优化是直接统计要消除的格子数,这样就不用遍历一遍.
坑爹的是行列公用,所以不要一找到就消除.
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 const int N = 6,M = 8,K = 10; 6 int m,mp[M][N],backup[N][M][N]; 7 int cnt[K],bcnt[N][K]; 8 bool st[M][N]; 9 struct Path{ 10 int x,y,d; 11 }path[N]; 12 bool check() 13 { 14 for(int i=1;i<=10;i++) 15 if(cnt[i]>0&&cnt[i]<3) return 0; 16 return 1; 17 } 18 void move(int x,int y,int ny) 19 { 20 swap(mp[x][y],mp[x][ny]); 21 while(1)//迭代是因为存在消掉又让一部分被削掉 22 {//处理悬空方块 23 for(int j=1;j<=5;j++) 24 { 25 int k = 0; 26 for(int i=1;i<=7;i++) 27 if(mp[i][j]) 28 { 29 mp[++k][j] = mp[i][j]; 30 } 31 for(int i=k+1;i<=7;i++) mp[i][j] = 0; 32 } 33 bool ok = 0; 34 memset(st,0,sizeof st); 35 for(int i=1;i<=7;i++) 36 for(int j=1;j<=5;j++) 37 if(mp[i][j]) 38 { 39 int k = i,r = i; 40 while(k+1<=7&&mp[k][j]==mp[k+1][j]) k++; 41 while(r-1>=1&&mp[r][j]==mp[r-1][j]) r--; 42 if(k-r+1>=3) 43 { 44 ok = 1; 45 st[i][j] = 1;//看此方隔要不要消掉 46 } 47 else{ 48 k = j,r = j; 49 while(k+1<=5&&mp[i][k]==mp[i][k+1]) k++; 50 while(r-1>=1&&mp[i][r]==mp[i][r-1]) r--; 51 if(k-r+1>=3) 52 { 53 ok = 1; 54 st[i][j] = 1; 55 } 56 } 57 } 58 if(!ok) break; 59 for(int i=1;i<=7;i++) 60 for(int j=1;j<=5;j++) 61 if(st[i][j])//这里实际没有处理掉下来的情况 62 { 63 cnt[0]--; 64 cnt[mp[i][j]]--; 65 mp[i][j] = 0; 66 } 67 } 68 } 69 bool dfs(int step) 70 { 71 if(!step) return !cnt[0]; 72 if(!check()) return 0; 73 memcpy(backup[step],mp,sizeof mp); 74 memcpy(bcnt[step],cnt,sizeof cnt); 75 for(int j=1;j<=5;j++) 76 for(int i=1;i<=7;i++) 77 { 78 if(!mp[i][j]) continue; 79 if(j+1<=5) 80 { 81 path[step] = {i-1,j-1,1}; 82 move(i,j,j+1); 83 if(dfs(step-1)) return 1; 84 memcpy(mp,backup[step],sizeof mp); 85 memcpy(cnt,bcnt[step],sizeof cnt); 86 } 87 if(j-1>=1&&!mp[i][j-1]) 88 { 89 path[step] = {i-1,j-1,-1}; 90 move(i,j,j-1); 91 if(dfs(step-1)) return 1; 92 memcpy(mp,backup[step],sizeof mp); 93 memcpy(cnt,bcnt[step],sizeof cnt); 94 } 95 } 96 return 0; 97 } 98 int main() 99 { 100 scanf("%d",&m); 101 for(int i=1;i<=5;i++) 102 { 103 int x,r = 0; 104 while(scanf("%d",&x)&&x) 105 mp[++r][i] = x,cnt[x]++,cnt[0]++; 106 }//cnt[0]记录要被消除的方块个数. 107 if(dfs(m)) 108 for(int i=m;i>=1;i--) 109 printf("%d %d %d\n",path[i].y,path[i].x,path[i].d); 110 else puts("-1"); 111 return 0; 112 }