Flip Game POJ1753解题报告?
一道(入门)状态压缩搜索题(然而newbie的我费了一小时)
具体过程看代码吧
第一次写感觉方法还是十分巧妙的
#include<iostream>
#include<cstring>
#define ll long long
using namespace std;
char tab[5][5];//存储棋盘
ll vis[5][5];//判断每个位置有无翻过
ll res=17;//棋子翻的最终次数 初始化为17是因为总共最多反转16个棋子 最终结果如果还是17则说明没有方案可以把这个棋盘全部翻成白色的(或黑色的);
ll ans=0;//在某一情况下需要多少步
int disy[5]={0,0,1,0,-1};//5种情况(还要判断它自身是否翻过
int disx[5]={0,1,0,-1,0};
bool is_black(ll y,ll x){//用来判断i,j点是否是黑色的函数
int n=0;
if(tab[y][x]=='b') n++;//如果i,j是黑色先把n+1
for(ll i=0;i<=4;i++){//判断上下左右和它本身是否被翻过面 (神奇的做法)
if(vis[y+disy[i]][x+disx[i]]==1&&y+disy[i]<=4&&x+disx[i]<=4)
n++;
}
if(n%2==1) return true;//如果是奇数则是黑色返回true
return false;//否则不是黑色
}
void dfsw(ll h){
ans=0;
for(ll i=1;i<=4;i++)//在当前状态的刚开始所有棋子都不翻
for(ll j=1;j<=4;j++)
vis[i][j]=0;
for(ll i=1;i<=4;i++){//发挥 h 的作用,把 h 所表示的数转化为第一层每一位上是否翻转的情况 如0001表示第一行第一个棋子翻,其他不翻
if(h&1==1){
vis[1][i]=1;ans++;
}
h>>=1;
}
for(ll i=2;i<=4;i++){//判断除第一行以外的棋子需不需要翻面
for(ll j=1;j<=4;j++){
if(is_black(i-1,j)){//如果i,j这个棋子的上面i-1,j是黑色的(或白色的)则翻面
vis[i][j]=1;ans++;
}
}
}
for(ll i=1;i<=4;i++){//判断最后棋盘有没有全变成一种颜色 如果有直接退出
if(is_black(4,i))
return;
}
if(ans<res) res=ans;//如果此答案比总答案小则... res=ans
}
void dfsb(ll h){//全变黑的搜索 同dfsw
ans=0;
ll op=0;
if(h==0) op=1;
for(ll i=1;i<=4;i++)
for(ll j=1;j<=4;j++)
vis[i][j]=0;
for(ll i=1;i<=4;i++){
if(h&1==1){
vis[1][i]=1;ans++;
}
h>>=1;
}
for(ll i=2;i<=4;i++){
for(ll j=1;j<=4;j++){
if(!is_black(i-1,j)){
vis[i][j]=1;ans++;
}
}
}
for(ll i=1;i<=4;i++)
if(!is_black(4,i))
return;
if(ans<res) res=ans;
}
int main(){
for(ll i=1;i<=4;i++){//读入棋盘数据
for(ll j=1;j<=4;j++){
cin>>tab[i][j];
}
}
for(ll i=0;i<16;i++){//i<16的原因是第一行总共就4个棋子 从0000到1111总共有15种情况
dfsw(i);//全变白和全变黑都试一遍
dfsb(i);
}
if(res==17) {cout<<"Impossible";return 0;}//输出最后的结果
cout<<res;
return 0;
}