Vijos P1301 智力游戏

【问题描述】
 
  这是一个非常有趣的智力游戏,它规则简单而又变化多端。
  游戏在一个m*n的方格里进行。游戏开始时所有的格子都是白色。游戏的目标是通过给出的线索按要求选择一些方格涂黑。这些线索是写在每一行和每一列前面的一些数字,它告诉了你该行或者该列应该出现的几组黑色方格各有多少个。 

  下面是一个简单的例子。在某一行中,线索给出了“2 1 4”三个数字,那么从这一行的某个位置起将依次出现两个相邻的黑色方格、若干白色方格、一个黑色方格、若干白色方格、四个连续的黑色方格,并且不再有其它的黑色方格。一种可能的情况如下图所示。 

                   

              
  你需要选择一些方格涂成黑色,使得这些黑色方格满足所有行和所有列的要求。 


  例如,下图给出了某个谜题的一个可能的解(第二列没有数字表示这一列没有黑色方格)。 


                                 
  给出这样一个谜题,请打印出该问题的解
 
【输入格式】
 
  第一行有两个用空格隔开的正整数m,n,他们分别表示该谜题的方格列数和行数。 
  接下来m行从左至右描述了谜题的纵向信息。每一行有若干个数字,这些数字表示该列从上至下将会出现的连续黑色方格数。每一行用一个数字0作为结束。 
  接下来n行从上至下描述了谜题的横向信息。每一行有若干个数字,这些数字表示该行从左至右将会出现的连续黑色方格数。每一行用一个数字0作为结束。 


 
【输出格式】
 
  将谜题的解打印出来。 
  每一个方格用两个字符表示。其中,一个白色方格用“ ”(两个空格)表示,一个黑色方格用“##”表示。因此,输出数据一共有n行,每行2m个字符。 
  我们保证输入数据有唯一解。
 
【输入样例】
 
4 3
2 0
0
1 1 0
2 0
1 0
1 1 0
1 2 0
 
【输出样例】
 
    ##
##        ##
##    ####


 
【数据范围】
 
对于30%的数据,m,n<=3; 
对于50%的数据,m,n<=10; 

对于100%的数据,m,n<=20。



分析:

这个一看就是回溯,但是重点是前前后后调了我大概总计6个小时?(ORZ)

之前还写过另外一个版本的,但是完全过不了两组大数据。。超时超时超上天!!!

然后就问了一下一个过了的同学他的思路,发现。。。写起来比我之前那个方法更短,而且更加好调试(知道怎么剪枝但是调不出来)。看来回溯的入手角度不好还是真的会要命啊。。。。。


来说一下真正的思路吧:回溯对象作为每个点,有两个选择,要么是黑色,要么是白色;
题目给足了信息表明只有唯一的解,那么这就变成了一个推理题:
对于一个格子,如果必须填黑色,当且仅当其所处的行或者列上有没有完成但是已经开始的序列;
如果必须填白色,当且仅当这个格子的左边或者上面有刚刚完成的序列;
如果一个格子既要填黑色又要填白色,那么这个方案就是矛盾的,返回;
如果一个格子两个颜色都可以填,那么就两种颜色都试一下;
最后的判定:如果把整个图都扫描完了都没有出现矛盾,那么就进行一次全图扫描判断结果是否合题意,如果符合,直接返回解输出答案;


代码如下:

#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=25;

int M,N,L[maxn][maxn],R[maxn][maxn];
int nowl[maxn],needl[maxn],finl[maxn],finr[maxn],cntl[maxn],cntr[maxn];
int A[maxn][maxn];

void data_in()
{
	scanf("%d%d",&M,&N);
	int x,cnt;
	for(int i=1;i<=M;i++)
		while(scanf("%d",&x)==1 && x) L[i][++cntl[i]]=x; 
	for(int i=1;i<=N;i++)
		while(scanf("%d",&x)==1 && x) R[i][++cntr[i]]=x;
	for(int i=1;i<=M;i++) finl[i]=1;
	for(int i=1;i<=N;i++) finr[i]=1;
}
void ans_out()
{
	for(int i=1;i<=N;i++)
	{
		for(int j=1;j<=M;j++)
		{
			if(A[i][j]) printf("##");
			else printf("  ");
		}
		printf("\n");
	}
}
bool run(int r,int l,int nowr,int needr)
{
	if(r>N)
	{
		int len=0,cnt=0;
		for(int i=1;i<=N;i++)//检查行上的要求 
		{
			cnt=0;
			for(int j=1;j<=M;j++) if(A[i][j])
			{
				cnt++,len=0;
				while(A[i][j] && j<=M) len++,j++;
				if(len!=R[i][cnt]) return 0;
			}
			if(cnt!=cntr[i]) return 0;
		}
		for(int j=1;j<=M;j++)//检查列上的要求 
		{
			cnt=0;
			for(int i=1;i<=N;i++) if(A[i][j])
			{
				cnt++,len=0;
				while(A[i][j] && i<=N) len++,i++;
				if(len!=L[j][cnt]) return 0;
			}
			if(cnt!=cntl[j]) return 0;
		}
		return 1;
	}
	if(l>M)
	{
		if(nowr==needr)
		{
			int len=0,cnt=0;
			for(int j=1;j<=M;j++) if(A[r][j])
			{
				cnt++,len=0;
				while(A[r][j] && j<=M) len++,j++;
				if(len!=R[r][cnt]) return 0;
			}
			if(cnt!=cntr[r]) return 0;
			finr[r]++;
			if(run(r+1,1,0,0)) return 1;
			finr[r]--;
		}
	}
	else if(nowrcntr[r]) return 0;
		nowl[l]++; 
		A[r][l]=1;
		if(run(r,l+1,1,R[r][finr[r]])) return 1;
		nowl[l]--;
		A[r][l]=0;
	}
	else if(nowl[l]!=0 && nowl[l]==needl[l])//r行无所谓,l列必须不填
	{
		nowl[l]=needl[l]=0;
		finl[l]++;
		A[r][l]=0;
		if(run(r,l+1,0,0)) return 1;
		finl[l]--;
		nowl[l]=needl[l]=L[l][finl[l]];
	}
	else//完全没有约束条件 
	{
		A[r][l]=0;
		if(run(r,l+1,0,0)) return 1;
		if(!(finr[r]>cntr[r] || finl[l]>cntl[l]))
		{
			A[r][l]=1;
			nowl[l]=1;
			needl[l]=L[l][finl[l]];
			if(run(r,l+1,1,R[r][finr[r]])) return 1;
			nowl[l]=needl[l]=0;
			A[r][l]=0;	
		}
	}
	return 0;
}
int main()
{
	freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);
	data_in();
	run(1,1,0,0);
	ans_out();
	return 0;
}

你可能感兴趣的:(Vijos P1301 智力游戏)