题目链接
题目大意
看了好久都没看懂题目意思是啥。。。
有12个孔,有些孔上面有障碍。若如果相邻的三个孔有两个孔被遮挡,并且被遮挡的两个孔相邻,则可以将中间的障碍拿掉,并将一端的遮挡物移到另一端没有被遮挡的孔上面。
简单来说:将这三个孔的状态反转。
例如记x为障碍遮住的孔,o为未遮住的孔 oxx -> xoo,xxo -> oox
题目思路
如果看懂了题目的话,这个题看一眼数据范围显然是记忆化搜索。但是巧妙运用异或操作可以使代码变的短很多。
一共12个孔,只有2^12=4096种情况,但是查询次数很多,我们可以考虑用二进制1和0表示‘o’和‘-’两种状态,进行状态压缩,用一个二进制数代表一种情况。然后再在记忆化搜索的过程中把已知情况的答案记录下来,方便下次直接用。
搜索的时候就找 ‘-oo’ 和 ‘oo-’,注意到它们有共同点:中间是1,两边不相同。“将中间的孔的遮挡物移开,将一端的遮挡物移到另一端没有被遮挡的孔上面。”其实就是把中间的1变为0,再把两边互换,对于二进制数011和110,我们只要将它^111即异或7(1和0异或上0不变,异或1则可互换)
易错
一定要注意x&i<<1 与x>>1&i 的区别
代码
#include<cstdio>
#include<algorithm>
using namespace std;
int t,f[1<<13];//记忆化数组
char s[20];
int dfs(int x){//如果这种状态已经搜过了直接返回答案
if(f[x]||!x){
return f[x];
}
int cnt=__builtin_popcount(x);//记录状态中1的个数
for(int i=1;i<=10;i++){//枚举'-oo' 和 'oo-'的右端
if((x&(1<<i))&&(((x>>(i-1))&1)^(x>>(i+1))&1)){//如果中间是1,两边不相同(异或为1)
cnt=min(cnt,dfs(x^(7<<(i-1))));//进行操作继续搜索
}
}
return f[x]=cnt;//记忆化
}
int main(){
scanf("%d",&t);
while(t--){
int x=0;
scanf("%s",s+1);
for(int i=1;i<=12;i++){
x=(x<<1)+(s[i]=='o'?1:0);//将状态压缩进二进制数x
}
printf("%d\n",dfs(x));
}
return 0;
}
参考文章https://blog.nowcoder.net/n/2c9df0646a6f4cb8a63788c9cbfa27cc