3422. 【NOIP2013模拟】水叮当的舞步

Description

水叮当得到了一块五颜六色的格子形地毯作为生日礼物,更加特别的是,地毯上格子的颜色还能随着踩踏而改变。

为了讨好她的偶像虹猫,水叮当决定在地毯上跳一支轻盈的舞来卖萌~~~

地毯上的格子有N行N列,每个格子用一个0~5之间的数字代表它的颜色。

水叮当可以随意选择一个0~5之间的颜色,然后轻轻地跳动一步,地毯左上角的格子所在的联通块里的所有格子就会变成她选择的那种颜色。这里连通定义为:两个格子有公共边,并且颜色相同。

由于水叮当是施展轻功来跳舞的,为了不消耗过多的真气,她想知道最少要多少步才能把所有格子的颜色变成一样的。

Input

每个测试点包含多组数据。

每组数据的第一行是一个整数N,表示地摊上的格子有N行N列。

接下来一个N*N的矩阵,矩阵中的每个数都在0~5之间,描述了每个格子的颜色。

N=0代表输入的结束。

Output

对于每组数据,输出一个整数,表示最少步数。

Sample Input

2

0 0 

0 0

3

0 1 2

1 1 2

2 2 1

0

Sample Output

0

3

Data Constraint

对于30%的数据,N<=5

对于50%的数据,N<=6

对于70%的数据,N<=7

对于100%的数据,N<=8,每个测试点不多于20组数据。

Solution

水叮当的舞步 From lydrainbowcat
类型:IDA* (迭代加深启发式搜索)
方法一:
枚举每次选取了哪种颜色,然后找出左上角的格子所在的联通块,改变颜色。
为了避免来回改变、搜索深度过大,采用迭代加深的dfs限制搜索步数。
迭代加深也就是,依次限制搜索深度为0、1、2、3…… 进行搜索,搜索过程中发现深度超过限制就马上退出。只要搜索成功就找到了答案,也可以立即退出。
期望得分:0~10分。
方法二:
加入一个小剪枝:如果改变颜色后,左上角格子所在的联通块大小没有改变,可以剪枝。这样可以避免来回往复地搜索。
期望得分:10~20分。
方法三:
采用IDA*算法,设计估价函数。可以发现如果当前矩阵中除了左上角的联通块之外,共有M种颜色,那么还需要的步数不小于M。因此如果当前搜索深度+估价函数的值>深度限制,可以回溯。
期望得分:50~70分。
方法四:
我们可以发现,每次寻找左上角的格子所在的联通块耗费的时间常数巨大。因此我们在这里寻求突破。
我们引入一个N*N的v数组。左上角的格子所在的联通块里的格子标记为1。左上角联通块周围一圈格子标记为2,其它格子标记为0。如果某次选择了颜色c,我们只需要找出标记为2并且颜色为c的格子,向四周扩展,并相应地修改v标记,就可以不断扩大标记为1的区域,最终如果所有格子标记都是1,那么显然找到了答案。
期望得分:90~100分。


Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 10
#define I int
#define F(i,a,b) for(I i=a;i<=b;i++)
#define mem(x,y) memset(x,y,sizeof(x))
using namespace std;
I n,m,a[N][N],bz[N][N],ok,tot,ans,v[7],fx[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
I read(){
	I x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x;
}
void dg(I c,I x,I y){
	bz[x][y]=1;
	F(k,0,3){
		I xx=x+fx[k][0],yy=y+fx[k][1];
		if(xx>0&&yy>0&&xx<=n&&yy<=n&&bz[xx][yy]!=1){
			bz[xx][yy]=2;
			if(c==a[xx][yy]) dg(c,xx,yy); 
		}
	}
}
void dfs(I x){
	I t=0;
	mem(v,0);
	F(i,1,n)
		F(j,1,n) if(!v[a[i][j]]&&bz[i][j]!=1) t+=(v[a[i][j]]=1);
	if(!t) ok=1;
	if(x+t>ans||ok) return;
	I b[N][N];
	F(c,0,5){
		b[0][0]=0;
		F(i,1,n)
			F(j,1,n) b[i][j]=bz[i][j];
		F(i,1,n)
			F(j,1,n){
				if(bz[i][j]==2&&a[i][j]==c){
					dg(c,i,j);
					b[0][0]=1;
				}
			}
		if(b[0][0]) dfs(x+1);
		F(i,1,n)
			F(j,1,n) bz[i][j]=b[i][j];
	}
}
I main(){
	n=read();
	while(n){
		ans=-1;ok=0;
		mem(bz,0);
		F(i,1,n)
			F(j,1,n) a[i][j]=read();
		dg(a[1][1],1,1);
		while(1){
			ans++;
			dfs(0);
			if(ok) break;
		}
		printf("%d\n",ans);
		n=read();
	}
	return 0;
}


作者:zsjzliziyang 
QQ:1634151125 
转载及修改请注明 
本文地址:https://blog.youkuaiyun.com/zsjzliziyang/article/details/98111638

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值