【JZOJ】3422. 水叮当的舞步

Description

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

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

地毯上的格子有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组数据。

思路

才开始想的时候,以为是贪心乱搞(觉得DFS过不去),后来发现就是个DFS(迭代加深)。
考虑每个联通块,很容易想到把其抽成一个点,将原方格图简化成一张无向图。
考虑普通爆搜,从左上角顶点所在联通块出发,暴力枚举下一步走什么颜色,更新可延伸点。

此处可以有一个简单的剪枝:对于所枚举颜色,它必须对答案造成贡献才可以;若无贡献,则不再向下。

然后加入迭代加深的思想,每次递增Ans值,限制DFS深度,直到DFS为合法状态。
不难发现Ans值即深度最多为16左右(极限情况)。

此时代码应该会TLE(30分)。

加入迭代加深常用剪枝:若剩余颜色数量大于剩下步数,直接就是不合法了。
此处优化会大幅提高代码速度,缘由是一般情况下最后几层宽度十分大,直接跳过确实会优化不少。

代码

#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=10;
const int MAXC=100;
int N,a[MAXN][MAXN],Ans;
int Cnt,cct[MAXN][MAXN];
int Col[MAXC],vis[MAXC][MAXC];
int Vis[MAXC],Len,h[MAXC];
int Vs[MAXC],Vt,e[MAXC];
vector<int>P[MAXC];queue<int>T1,T2;
int f[10][5]={{0,1},{1,0},{-1,0},{0,-1}};
bool cmp(int a,int b){return Col[a]<Col[b];}
int Colcnt[MAXC],Colend[MAXC];
void bfs(int x,int y){
	cct[x][y]=++Cnt;
	Col[Cnt]=a[x][y];
	while(!T1.empty())T1.pop();
	while(!T2.empty())T2.pop();
	T1.push(x);T2.push(y);
	while(!T1.empty()){
		x=T1.front();T1.pop();
		y=T2.front();T2.pop();
		for(int i=0;i<4;i++){
			int w1=x+f[i][0];
			int w2=y+f[i][1];
			if(w1>=1&&w1<=N&&w2>=1&&w2<=N)
				if(!cct[w1][w2]&&a[x][y]==a[w1][w2]){
					cct[w1][w2]=Cnt;
					T1.push(w1);
					T2.push(w2);
				}
		}
	}
}
void Init(){
	for(int i=0;i<=Cnt;i++)P[i].clear();
	Cnt=Ans=Len=Vt=0;
	memset(h,0,sizeof(h));
	memset(Vs,0,sizeof(Vs));
	memset(cct,0,sizeof(cct));
	memset(vis,0,sizeof(vis));
	memset(Col,0,sizeof(Col));
	memset(Vis,0,sizeof(Vis));
	for(int i=1;i<=N;i++)
		for(int j=1;j<=N;j++)
			if(!cct[i][j])
				bfs(i,j);
	for(int x=1;x<=N;x++)
		for(int y=1;y<=N;y++)
			for(int k=0;k<4;k++){
				int w1=x+f[k][0];
				int w2=y+f[k][1];
				if(w1>=1&&w1<=N&&w2>=1&&w2<=N)
					if(a[x][y]!=a[w1][w2])
						if(!vis[cct[x][y]][cct[w1][w2]]){
							vis[cct[x][y]][cct[w1][w2]]=1;
							vis[cct[w1][w2]][cct[x][y]]=1;
							P[cct[x][y]].push_back(cct[w1][w2]);
							P[cct[w1][w2]].push_back(cct[x][y]);
						}
			}
	for(int i=1;i<=Cnt;i++)
		Colend[Col[i]]++;
}
int Get(){
	int ret=0;
	for(int i=0;i<6;i++)
		if(Colcnt[i]<Colend[i])
			ret++;
	return ret;
}
int Dfs(int dep){
	if(dep>Ans+1)return 0;
	if(Vt==Cnt)return 1;
	int t=Len,tt=Vt;
	for(int c=0;c<6;c++){
		for(int i=1;i<=t;i++)
			if(Col[h[i]]==c){
				if(Vs[h[i]])continue;
				Vs[h[i]]=1;e[++Vt]=h[i];
				Colcnt[Col[e[Vt]]]++;
				int size=P[h[i]].size();
				for(int g=0;g<size;g++){
					int v=P[h[i]][g];
					if(Vis[v])continue;
					Vis[v]=1;h[++Len]=v;
				}
			}
		if(tt!=Vt&&Get()+dep<Ans+1)
			if(Dfs(dep+1))
				return 1;
		for(int i=t+1;i<=Len;i++)Vis[h[i]]=0;
		for(int i=tt+1;i<=Vt;i++){
			Colcnt[Col[e[i]]]--;
			Vs[e[i]]=0;
		}
		Len=t;Vt=tt;
	}
	return 0;
}
int main(){
	while(~scanf("%d",&N)&&N){
		for(int i=1;i<=N;i++)
			for(int j=1;j<=N;j++)
				scanf("%d",&a[i][j]);
		Init();h[++Len]=1;
		Vis[1]=1;Vt=0;
		while(!Dfs(0)){
			memset(Vis,0,sizeof(Vis));
			memset(Vs,0,sizeof(Vs));
			Vis[1]=1;Ans++;
		}
		printf("%d\n",Ans);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值