一共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));
    }
}