【搜索优化】翻转游戏 flip

翻转游戏
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;
}








  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值