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 }        
View Code

然后第四种。我们可以发现其实只枚举第一行如何翻转和期望达到的黑/白状态,之后每一行为了保持全黑或全白,只有唯一的翻转可能。这样就剪掉了很多不必要的搜索量。

技术分享
 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 }
View Code

BFS方法和DFS大同小异,不再赘述。对于类似问题,状压+搜索就足以解决大部分。

以上是关于poj 1753 枚举+暴搜的主要内容,如果未能解决你的问题,请参考以下文章

POJ1753 Flip Game(bfs枚举)

POJ 1753 (枚举+DFS)

POJ_1753——Flip Game(枚举)

poj1753 Flip Game(枚举Enum+dfs)

POJ1753Flip Game(DFS + 枚举)

POJ 1753(枚举)