POJ 4007 Flood-it!

玩一玩?

题目大意,多组样例输入,一个边长为n的数字矩阵,由0~5的数字构成,你的任务是用最少的步数使图中颜色一样,游戏中点击任意颜色的方块都会使左上角的联通块变色,说不明白,语文不好,看图吧


左上现在是一个黄色的联通块,我如果点击图中任意一个紫色,他就会变成如下

呐,紫色了,现在他们三个算一个联通块,我点击什么颜色这个联通块就会变成什么颜色

然后显然,直接爆搜会炸,所以IDA*,限制深度,暴力搜索点击每个颜色

说一下方法

初始化需要map[][]存图,vis[][]染色,jud[]判断

第一步我们把与左上角联通的且颜色一样的块设成vis[x][y]=1,说明颜色一样,然后不一样,说明下次染色可能会和改点颜色相同,设成vis[x][y]=2

第二步,判断颜色是否相同以及夹杂着的剪枝,两重for循环,若当前坐标vis不为1,则还没染色,判断这个点的颜色有没有被统计过,没有就flag++,因为显然,当前图中剩余几种颜色,那么最少就需要这么多步来使颜色一样,剪枝就是当前搜索深度加上flag>limit,那么当前染色方式不存在解,return

第三步,剪枝,除上面那个剪枝外,还有一个剪枝,就是如果当前搜索颜色染完色后,联通块没有扩大,那么当前步无意义,continue

第四步,染色,与联通块接壤的并且和当前染得颜色一样的,就可以加入联通块中,vis变成1,颜色不同掰成2

第五步,回溯,需要一个容器数组,用memcpy,存储当前vis,搜索完后,没有结果,再把当前状态还给vis

OK,就这么多,然后,我emm,一开始jud在check里,但是会wa,开在外面,每次清空就没事,茫然

代码

By Acer.Mo
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=500500;
int n,col=0,lim;
int map[10][10];
int vis[10][10],jud[10];
int fx[5]={0,0,1,-1},fy[5]={1,-1,0,0};
int check()
{
	int fuck=0;//统计剩余颜色
	fill(jud,jud+10,0);
	for (int i=1;i<=n;i++)
	for (int k=1;k<=n;k++)
	if (vis[i][k]!=1&&jud[map[i][k]]==0) fuck++,jud[map[i][k]]=1;//一种颜色统计一次
	return fuck;
}
void paint(int x,int y,int c)//染色
{
	vis[x][y]=1;
	for (int i=0;i<4;i++)
	{
		int xx=x+fx[i];
		int yy=y+fy[i];//四个方向
		if (xx<1||yy<1||xx>n||yy>n||vis[xx][yy]==1) continue;//越界
		if (map[xx][yy]==c) paint(xx,yy,c);//颜色相同继续扩展
		else vis[xx][yy]=2;//否则标记为待更新
	}
	return ;
}
bool orz(int qlm)//剪枝,步骤3
{
	int fuck=0;
	for (int i=1;i<=n;i++)
	for (int k=1;k<=n;k++)
	if (map[i][k]==qlm&&vis[i][k]==2)//待扩展点,与当前搜索颜色相同
	paint(i,k,qlm),fuck=1;//可以扩展,剪枝同时给图染色
	return fuck;//若没有扩展则返回零
}
bool IDAS(int limdep)
{
	if (limdep==lim) return !check();//搜索到限制深度,返回check,即图中是否省零个未染色点
	if(limdep+check()>lim) return 0;//剪枝,上面	
	for (int i=0;i<=5;i++)
	{
		int emm[10][10];
		memcpy(emm,vis,sizeof(vis));//注意sizeof里
		if (!orz(i)) continue;//剪枝
		if (IDAS(limdep+1)) return 1;//染色完成
		memcpy(vis,emm,sizeof(emm));//回溯
	}
	return 0;
}
int main()
{
	while (~scanf("%d",&n)&&n)
	{
		memset(vis,0,sizeof(vis));
		for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
		scanf("%d",&map[i][j]);
		paint(1,1,map[1][1]);
		for (lim=check();;lim++) //从初始颜色个数开始,最少需要这么多步
		if (IDAS(0)) break;
		printf("%d\n",lim);
	}	
	return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值