Flip Game(2021-TRN1-K)
题目大意
这个和poj 1222有点类似(其实那道题更难一点)
大意是说,在一个4*4的棋盘上面(姑且称作棋盘)的每一个格子里都放着一个棋子。棋子比较特殊,一面是黑的,一面是白的。可以通过翻棋子来改变棋子的颜色。每翻一个棋子,这个棋子的颜色肯定变了,而该棋子周围的棋子也会随之变颜色。
求最少的步骤。
题目解析
典型的DFS,不需要太多的剪枝。这个论断的得出是因为DFS的本质是枚举。而显然,每一个棋子我们都可以选择翻开或者不翻开,一共就有2^16次方种可能。这个计算量是很小的。而poj 1222那道题格子更多,暴力DFS可能就会超时了。
怎么枚举呢?就是从左到右,从上到下。枚举两种情况:翻开了,或者不翻开来。这种枚举不是没见过。然后回溯。
然而做的时候还是捉襟见肘,居然一开始还没有意识到有的棋子可以不翻开 还有就是写DFS有一段时间没写,手生了,连step作为函数参数,还有哪些要回溯一开始都没有想清楚,人没了。。。
代码
#include <bits/stdc++.h>
using namespace std;
int a[5][5];
char str[5][5];
int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int mini=1e8;
int check()
{
int i,j,sum=0;
for(i=1;i<=4;i++)
{
for(j=1;j<=4;j++)
{
sum+=a[i][j];
}
}
if(sum==0||sum==16) return 1;
return 0;
}
void flip(int x,int y)
{
a[x][y]^=1;
for(int k=0;k<4;k++)
{
int newx=x+dir[k][0];
int newy=y+dir[k][1];
if(newx>=1&&newx<=4&&newy>=1&&newy<=4)
{
a[newx][newy]^=1;
}
}
}
void dfs(int x,int y,int step)
{
if(step>16) return;///16个格子都翻过了,不用继续深搜
if(check())
{
if(step<mini) mini=step;
return;
}
if(y>=5)
{
x++;
y=1;
}
if(x>=5) return;
flip(x,y);
dfs(x,y+1,step+1);///翻开棋子
flip(x,y);///回溯
dfs(x,y+1,step);///不翻开棋子
}
void init()
{
int i,j;
for(i=1;i<=4;i++)
{
for(j=0;j<4;j++)
{
if(str[i][j]=='b') a[i][j+1]=1;
else a[i][j+1]=0;
}
}
}
int main()
{
int i;
for(i=1;i<=4;i++)
{
gets(str[i]);
}
init();
dfs(1,1,0);
if(mini<=16) printf("%d\n",mini);
else printf("Impossible\n");
return 0;
}