poj 1753 枚举+暴搜
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了poj 1753 枚举+暴搜相关的知识,希望对你有一定的参考价值。
很基础的题,但是有很多种搜索姿势,感觉用来入门还是很好的。
题意:有一个4*4的棋盘,棋盘上有黑白格,每一次你可以翻其中的一个格子。一个格子(x,y)如果被翻,它相邻的前后左右四个格子(如果在棋盘上)也要翻转。现在给你一个初始的棋盘状态,问把这个棋盘翻转到全黑或全白的最少次数;若不能达到全黑或全白,输出Impossible。
只有4*4的棋盘,同时格子只有黑白两面。对于同一个格子,翻两次和不翻没有区别。很小的数据量,就是引导人状压之后去暴力做的。但是你可以枚举全部65536种状态不断更新答案;也可以从翻0个格子-翻1个格子-…-翻16个格子这样递增的枚举答案,符合条件跳出;也可以用BFS去搜;也可以用DFS不断更新答案。
这里挑第一种和最后一种来说。
先说第一种傻瓜算法,代码量很短,跑起来很长。把所有可能翻的状态(1<<16种)枚举出来全过一遍也是松松的可以在时限内完成。亲测204MS。
1 #include <iostream> 2 using namespace std; 3 const int inf = 0x7fffffff; 4 char s[4][4]; 5 int cs[16] = {0x13,0x27,78,140,305,626,1252,2248,4880,8992,20032,35968,12544,29184,58368,51200};//保存翻第i格子的变化值 6 int po[16] = {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};//保存2^i 7 int main() 8 { 9 int value = 0; 10 int cmin = inf; 11 char c; 12 for (int i=0; i<16; i++) { 13 cin >> c; 14 if (c == ‘b‘) 15 value += (int)po[i]; 16 else 17 continue; 18 } 19 for (int i=0; i<65536; i++) {//枚举所有状态 20 int cou = 0; 21 int cvalue = value; 22 for (int j=0;j<16; j++) 23 if (i & (int)po[j]) { 24 cou++; 25 cvalue ^= cs[j]; 26 } 27 if (cvalue == 0 || cvalue == 65535)//全黑或全白 28 if (cou < cmin) 29 cmin = cou; 30 } 31 if (cmin == inf) cout << "Impossible"; 32 else cout << cmin << endl; 33 return 0; 34 }
然后第四种。我们可以发现其实只枚举第一行如何翻转和期望达到的黑/白状态,之后每一行为了保持全黑或全白,只有唯一的翻转可能。这样就剪掉了很多不必要的搜索量。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <queue> 5 using namespace std; 6 int dx[] = {0, 0, 0, 1, -1}; 7 int dy[] = {0, 1, -1, 0, 0}; 8 int ans = 0x3f3f3f3f; 9 int flip(int xx, int yy, int tmp)//依次翻转五个格子 10 { 11 for (int i=0; i<5; i++) { 12 int x = xx + dx[i], y = yy + dy[i]; 13 if (x >=0 && x <4 && y>=0 && y < 4) 14 tmp ^= 1 << (x * 4 + y); 15 } 16 return tmp; 17 } 18 //cur当前行,num当前步数,state当前棋盘状态,flag表示期望黑或白 19 void dfs(int cur, int num, int state, int flag) 20 { 21 if (cur == 4 && (state == 0xffff || state == 0)) {//更新答案 22 ans = min(ans, num); 23 } 24 if (cur == 4) return;//返回条件 25 int i = cur - 1; 26 for (int j=0; j<4; j++) {//使上一行全黑/白 27 if (((state & (1 << (i * 4 + j))) >> (i * 4 + j)) ^ flag) { 28 state = flip(cur, j, state); 29 num++; 30 continue; 31 } 32 } 33 dfs(cur + 1, num, state, flag); 34 } 35 void solve(int st) 36 { 37 for (int i=0; i<16; i++) {//枚举第一行 38 int num = 0, state = st; 39 for (int j=0; j<4; j++) { 40 if (i & (1 << j)) { 41 state = flip(0, j, state); 42 num++; 43 } 44 } 45 dfs(1, num, state, 0); 46 dfs(1, num, state, 1); 47 } 48 if (ans == 0x3f3f3f3f) 49 puts("Impossible"); 50 else printf("%d\n", ans); 51 } 52 int main() 53 { 54 char s[8]; 55 int st = 0; 56 for (int i=0; i<4; i++) { 57 scanf("%s", s); 58 for (int j=0; j<4; j++) { 59 st <<= 1; 60 st += (s[j] == ‘b‘) ? 1 : 0; 61 } 62 } 63 solve(st); 64 return 0; 65 }
BFS方法和DFS大同小异,不再赘述。对于类似问题,状压+搜索就足以解决大部分。
以上是关于poj 1753 枚举+暴搜的主要内容,如果未能解决你的问题,请参考以下文章