【NOIP2013模拟】水叮当的舞步

题目

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

分析

我们迭代加深,枚举答案限制,
BFS枚举每次选择哪种颜色,当枚举次数超过限制,就退出。否则如果BFS出答案就输出。
这样只能拿10分,
加上IDA,估价函数为图中大颜色数减一,
当枚举的深度加上估价函数大于限制就退出,
接着发现在枚举左上角所在的联通快时很浪费时间,
我们用一个一个N
N的v标记数组。左上角的格子所在的联通块里的格子为1,左上角联通块周围一格的格子为2,其它格子都为为0。如果某次选择了颜色c,我们只需要找出标记为2并且颜色为c的格子,向四周扩展,再修改v标记,就可以不断修改标记,但所有格子都为1,就是答案了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
const int maxlongint=2147483647;
using namespace std;
int n,m,ans;
int z1[4][2]={{1,0},{0,1},{-1,0},{0,-1}},f,a[9][9],t;
int mark[9][9];
bool colour[6];
int makeh()
{
	int h1=0;
	memset(colour,true,sizeof(colour));
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			if((mark[i][j]!=1 && colour[a[i][j]])) 
			{
				colour[a[i][j]]=false;
				h1++;
			}
		}
	return h1;
}
void put(int x,int y,int z)
{
	mark[x][y]=1;
	for(int i=0;i<=3;i++)
	{
		int xx=x+z1[i][0],yy=y+z1[i][1];
		if((xx<1 || yy<1 || xx>n || yy>n || mark[xx][yy]==1)) continue;
			if(a[xx][yy]==z)
				put(xx,yy,z);
			else
				mark[xx][yy]=2;
	}
}
bool q(int color)
{
	bool p=false;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			if((mark[i][j]==2 && a[i][j]==color))
			{
				p=true;
				put(i,j,color);
			}
		}
	return !p;
}
void dg(int g)
{
	int h=makeh();
	if(!h) 
	{
		t=true;
		return;
	}
	if(g+h>f) return;
	int copy1[9][9];
	memcpy(copy1,mark,sizeof(copy1));
	for(int color=0;color<=5;color++)
	{
		if(!q(color) && !t)
			dg(g+1);
		memcpy(mark,copy1,sizeof(mark));
	}
}
int main()
{
	while(1)
	{
		scanf("%d",&n);
		if(!n) break;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
			{
				scanf("%d",&a[i][j]);
			}
		memset(mark,0,sizeof(mark));
		put(1,1,a[1][1]);
		for(f=0;;f++)
		{
			t=0;
			dg(0);
			if(t)
				break;
		}	
		printf("%d
",f);
	}
}
原文地址:https://www.cnblogs.com/chen1352/p/9045331.html