[NOIP2013模拟]水叮当的舞步(暴力,IDA*)

题面

题目描述

水叮当得到了一块五颜六色的格子形地毯作为生日礼物,更加特别的是,地毯上格子的颜色还能随着踩踏而改变。
为了讨好她的偶像虹猫,水叮当决定在地毯上跳一支轻盈的舞来卖萌~~~
地毯上的格子有 N N N N N N列,每个格子用一个 0   5 0~5 0 5之间的数字代表它的颜色。
水叮当可以随意选择一个 0   5 0~5 0 5之间的颜色,然后轻轻地跳动一步,地毯左上角的格子所在的联通块里的所有格子就会变成她选择的那种颜色。这里连通定义为:两个格子有公共边,并且颜色相同。
由于水叮当是施展轻功来跳舞的,为了不消耗过多的真气,她想知道最少要多少步才能把所有格子的颜色变成一样的。

输入格式

每个测试点包含多组数据。
每组数据的第一行是一个整数N,表示地摊上的格子有N行N列。
接下来一个N*N的矩阵,矩阵中的每个数都在0~5之间,描述了每个格子的颜色。
N=0代表输入的结束。

输出格式

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

数据规模

对于30%的数据,N<=5
对于50%的数据,N<=6
对于70%的数据,N<=7
对于100%的数据,N<=8,每个测试点不多于20组数据。

做题过程

看完题目当场去世…因为蒟蒻已经将近一年没有写过这种无脑的大暴力了…
于是开始想方法绕开搜索…
最终瞎yy出来了一个 O ( ( n + 1 ) n T ) O({(n+1)}^nT) O((n+1)nT)的状压+双端队列BFS的做法,然后写完没多久就被自己给hack掉了…
时间已经过去了三个小时…蒟蒻终于忍心放下了这道题,然后发现…后面两道题都是水题,然后就没有然后了…

题解

其实自己对这类复杂度玄学的题目并没有什么好的看法…
做法其实相当简单,但写起来也需要一定的小技巧。

其实看到这道题首先显然会想到BFS,然而我们发现BFS需要保存上一次所访问过的一个 8 × 8 8\times 8 8×8的vis数组,这样做起来比较麻烦…

更好的做法是IDA*,我们可以借鉴BFS的思路,每次BFS限制一下深度,这样我们就能够很方便的继承上一层的信息。

IDA*的优点在于它可以剪枝,最显然的剪枝就是当颜色数+当前深度大于限定深度时,直接退出即可。

至于为什么IDA*可以通过本题,是因为本题的颜色数量相当的少,因此DFS最多是一个6叉完全数,注意到每一层都会是之前前面几层的节点数总和的几倍,因此我们针对深度大的节点进行剪枝,会得到相当好的效果。

时间复杂度: O ( 能 过 ) O(能过) O()

实现

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<vector>
using namespace std;
#define MAXN 9
#define Pr pair<int,int>
#define X first
#define Y second
int n,a[MAXN+1][MAXN+1];
int vis[MAXN+1][MAXN+1];
int px[4]={1,-1,0,0},py[4]={0,0,1,-1};
int tmp[MAXN+1],F,d;
void color(int x,int y,int col)
{
	vis[x][y]=1;
	for(int i=0;i<4;i++){
		int nx=x+px[i],ny=y+py[i];
		if(nx<1||ny<1||nx>n||ny>n||vis[nx][ny]==1)continue;
		vis[nx][ny]=2;
		if(a[nx][ny]==col)color(nx,ny,col);
	}
}
int rmcol(){
	int res=0;
	memset(tmp,0,sizeof(tmp));
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		if(vis[i][j]!=1&&!tmp[a[i][j]])
		tmp[a[i][j]]=1,res++;
	return res;
}
bool check(int col) {
    int flag=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
		if(a[i][j]==col&&vis[i][j]==2)
		flag=1,color(i,j,col);
    return flag;
}
void dfs(int x)
{
	int val=rmcol(),tp[MAXN+1][MAXN+1];
	//printf("%d::%d %d %d\n",d,x,val,F);
	if(val==0){F=1;return ;}
	if(val+x>d)return ;
	for(int i=1;i<=6;i++){
		memcpy(tp,vis,sizeof(tp));
		if(check(i))dfs(x+1);
		memcpy(vis,tp,sizeof(vis));
		if(F)return ;
	}
}
int main()
{
	while(~scanf("%d",&n)&&n)
	{
		F=0;memset(vis,0,sizeof(vis));
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
			{scanf("%d",&a[i][j]);a[i][j]++;}
		color(1,1,a[1][1]);
		d=0;
		while(1){
			dfs(0);
			if(F){printf("%d\n",d);break;}
			d++;
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值