翻转游戏
flip.pas/c/cpp
3S/256MB
【题目描述】
lxhgww在一个n*n的棋盘上进行一个翻转游戏。棋盘的每个格子上都放有一个棋子,每个棋子有2个面,一面是黑色的,另一面是白色的。初始的时候,棋盘上的棋子有的黑色向上,有的白色向上。现在lxhgww想通过最少次数的翻转,使得棋盘上所有的棋子都是同一个颜色向上的(即全是黑色向上的,或全是白色向上的)。每次翻转的时候,lxhgww可以选择任意一个棋子,将它翻转,同时,与它上下左右分别相邻的4个棋子也必须同时翻转。
【输入】
输入文件:flip.in
输入的第一行是一个整数n,表示棋盘是n*n的,
接下来有n行,每行包括n个字母,表示初始的棋盘状态。如果字母是w,则表示这个棋子当前是白色向上的,如果字母是b,则表示这个棋子当前是黑色向上的。
【输出】
输出文件:flip.out
输出为一行,如果无法翻转出目标状态,则输出“Impossible”,否则输出一个整数,表示lxhgww最少需要翻转的次数。
【输入样例】
4
bwwb
bbwb
bwwb
bwww
【输出样例】
4
【数据范围】
对于30%的数据,1<=n<=4
对于100%的数据,1<=n<=16
一开始还是在是想不出来好的办法,也就只有裸搜了。。。。。。
后来看了题解才恍然大悟,其实题解只有一句话:第一行确定了后面都确定了。
后来一想也是,我们可以找出第一行所有可行的方案,下面一个例子
原图: bwb wbw wwb
我们看,第一行我们可以得到所有方案,假设得到一个序列A,然后我们就可以先看如果要全部变成w需要改变第二行的那些,然后依次改变后面的
假设第一行我们得到了一个方案,为 bwb
那么我们要把它变成w,就需要改变第二行的第一个和第三个才能使第一行全部为w(至于也改变了后面的我们不管,因为每一行我们可以由它的下一行确定),那么这样操作完了后,我们只有最后一行没有确定了
这样就可以从全图搜索变成只搜第一行了,即使是深搜O(2N)也能过了!
C++ Code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define copymap(a,b) for(int _i=0;_i<n;_i++)for(int _j=0;_j<n;_j++)a[_i][_j]=b[_i][_j];
const int MAXN=30;
const int dx[]={0,0,1,-1,0};
const int dy[]={1,-1,0,0,0};
int n;
int map[MAXN][MAXN];
int oldmap[MAXN][MAXN];
int firstmap[MAXN][MAXN];
int a[MAXN];
int ans=0x7fffffff;
void read()
{
freopen("flip.in","r",stdin);
freopen("flip.out","w",stdout);
scanf("%d\n",&n);
char s[30];
for(int i=0;i<n;i++)
{
scanf("%s\n",s);
for(int j=0;j<n;j++)
map[i][j]=((s[j]=='b')?1:0);
}
copymap(firstmap,map);
}
void change(int x,int y)
{
for(int k=0;k<5;k++)
{
int nx=x+dx[k];
int ny=y+dy[k];
if(nx>=n||nx<0) continue;
if(ny>=n||ny<0) continue;
map[nx][ny]=1-map[nx][ny];
}
}
void turn(int sum,int k)
{
for(int i=1;i<n;i++)
for(int j=0;j<n;j++)
if(map[i-1][j]!=k)
{
change(i,j);
sum++;
}
bool flag=true;
for(int j=0;j<n;j++)
if(map[n-1][j]!=k) {flag=false;break;}
if(flag) ans=min(ans,sum);
}
void dfs(int x,int sum)
{
if(x>=n)//边界
{
for(int i=0;i<n;i++)
if(a[i]) change(0,i);
copymap(oldmap,map);
turn(sum,1);
copymap(map,oldmap);
turn(sum,0);
copymap(map,firstmap);
return;
}
a[x]=1;
dfs(x+1,sum+1);
a[x]=0;
dfs(x+1,sum);
}
void work()
{
dfs(0,0);
if(ans==0x7fffffff) printf("Impossible");
else printf("%d\n",ans);
}
int main()
{
read();
work();
return 0;
}