一共12个孔,只有2^12=4096种情况,但是查询次数很多,我们可以考虑用二进制1和0表示‘o’和‘-’两种状态,进行状态压缩,用一个二进制数代表一种情况。然后再在记忆化搜索的过程中把已知情况的答案记录下来,方便下次直接用。
搜索的时候就找 '-oo' 和 'oo-',注意到它们有共同点:中间是1,两边不相同。“将中间的孔的遮挡物移开,将一端的遮挡物移到另一端没有被遮挡的孔上面。”其实就是把中间的1变为0,再把两边互换,对于二进制数011和110,我们只要将它^111即异或7(1和0异或上0不变,异或1则可互换)
#include<cstdio> using namespace std; #define min(a,b) (a<b ? a:b) int f[1<<12];//记忆化数组 /*调试用,输出二进制状态 inline void out(int a){ for(int i=11;i>=0;i--) if (a>>i&1) printf("1"); else printf("0"); puts(""); } */ int dfs(int x) { if (f[x] || !x) return f[x];//如果这种状态已经搜过了直接返回答案 int cnt=0; for (int i=0; i<12; i++) if (x>>i&1) cnt++;//记录状态中1的个数 for (int i=0; i<10; i++)//枚举'-oo' 和 'oo-'的右端 if (x>>(i+1)&1 && x>>i&1 ^ x>>(i+2)&1)//如果中间是1,两边不相同(异或为1)。注意运算符优先级,这里由高到低分别是()、+、>>、&、^ cnt=min(cnt,dfs(x^7<<i));//进行操作继续搜索 return f[x]=cnt;//记忆化 } int main() { int n,x; scanf("%d",&n); f[0]=0; for(char s[12]; n; n--) { scanf("%s",s),x=0; for (int i=0; i<12; i++) x=(x<<1)+(s[i]=='o' ? 1:0);//将状态压缩进二进制数x printf("%d\n",dfs(x)); } }