纵横填字游戏解题报告

题意描述:

【问题描述】

       这个题目要求你编写一个程序来解决一个纵横填字游戏。

           这个游戏比我们在报纸上见到的通常的填字游戏要简单。游戏仅给出单词的起始位置,方面(横向或纵向)以及单词的长度。只要单词的长度正好,游戏中能填入任何一个来自词典的单词。

    在游戏中单词相交处的字母必须相同,当然,任何单词只准使用一次。

    思考一下以下这个游戏。

例如,假定从上到下有5行,用0到4来表示,从左到右有5列,用0到4来表示。我们用(X, Y)来表示填字游戏中第X列和第Y行的字母。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

在这个游戏中,我们需填入5个单词:在(0, 0)的右边填入一个4个字母的单词,在(0, 0)的下方填入一个4个字母的单词,在(2, 0)的下方填入一个4个字母的单词,在(0, 2)的右边填入一个3个字母的单词,最后在(2, 3)的右边填入一个3个字母的单词。字典上所有的单词都能使用但最多只能使用一次。例如,以下是一个可能的解决方案。

(0, 0)右边,LATE

(0, 0)下面,LIED

(2, 0)下面,TELL

(2, 3)右边,LOW

【输入】

    输入文件的第一行是作为字典使用的—个文本文件名,你可以假定这个文件存在,是可读的并且包含最多不超过100000个单词,并且按词典顺序存储,每行一个单词。字典中所有的单词所含的字母可以是大写或小写(在这个问题中字母的大小写是无关紧要的)。你可以假设字典中所有单词的长度不超过20个字符。输入文件的下一行包含一个整数n(n≤15),表示要填的单词的数量。接下来的n行中每行给出关于一个单词的提示,在每个提示中分别给出单词的首字母在填字游戏中的列和行的位置,后面根据单词的方向是横向还是纵向,相应跟字符A或字符D,最后一个数表示该单词的长度,以上数据之间均用空格隔开。

    你能作以下的进一步的假设。

    (1)填字游戏的尺寸不超过10×10。

    (2)游戏盘中放得下所有的单词。

    (3)用给定的词典是能够破解出该游戏的。

【输出】

    输出文件应该包含n行,输出游戏中可填入的所有单词。单词应该每行出现一个,并且按输入文件中提示的顺序输出。每个单词中所有的字母必须是大写的。所有的单词必须来自给定的词典文件(忽略大小写)。任何单词只能使用一次。对于给定输入文件可能有大量的正确解决方案,你只须输出其中的任意一个解决方案。

【样例】

       puzzle.in                                     puzzle.out

       words.txt                                          LATE

       5                                               LIED

       00 A 4                                       TELL

       00 D 4                                      EEL

       20 D 4                                      LOW

       02 A 3

       23 A 3


解题思路:

真的不会做这道题,所以看了老李的题解,发现实在太神了,写了很久才才对,又发现漏了一个优化。

这真的是一道很神的DFS,主要剪枝来自两个方向:

1、Hash出当前可以插入的所有字串:在处理当前串的时候,这个串中会有一些格子是已经被填过的,有一些格子是没被填过的,利用这个特性设计一个Hash函数使得被填过格子相同的待填字串都在一个桶里,预处理出每一个待填字串在填每一个串时的Hash值即可。

2、调整搜索顺序,使得被搜的前几个字串之中有尽可能多的重叠,以减小搜索树的形态。

#include<iostream>
#include<cstdlib>
using namespace std;
#include<cstdio>
#include<cstring>
#include<ctime>
#include<algorithm>
const int P=1000007;
char c[15];
int num[11],n,x[15],y[15],len[15],a[10][10];
bool flag[11][100000],p[15][10];
struct S{
	S * next;
	int len,k;
}hash[15][P];
struct DS{
	int x,y;
}b[2]={(DS){1,0},(DS){0,1}};
struct SS{
	char s[12];
	int i;
	inline bool operator < (SS a) const{
		return strcmp(s,a.s)<0;
	}
}s[11][100000],ans[15];
inline void dfs(int I){
	if(I==n){
		int tot=0;
		for(int i=1;i<11;++i)
			for(int j=0;j<num[i];++j)
				if(flag[i][j])
					ans[tot++]=s[i][j];
		while(--tot+1){
			for(int j=0;j<strlen(ans[tot].s);++j)
				putchar(ans[tot].s[j]+'A'-1);
			putchar('\n');
		}
		exit(0);
	}
	int X,Y,k,d,now,i,l;
	now=0;
	for(X=x[I],Y=y[I],d=0;d<len[I];++d,X+=b[c[I]].x,Y+=b[c[I]].y)
		if(p[I][d])
			now=(now*27+a[X][Y])%P;
	for(S * j=hash[I][now].next;j;j=j->next){
		l=j->len,k=j->k;
		if(flag[l][k])continue;
		for(X=x[I],Y=y[I],d=0;d<len[I];++d,X+=b[c[I]].x,Y+=b[c[I]].y)a[X][Y]=s[l][k].s[d];
		flag[l][k]=1;
		dfs(I+1);
		flag[l][k]=0;
	}
}
int main(){
	/*---------Read---------*/
	FILE * fin=fopen("puzzle.in","r");
	freopen("puzzle.out","w",stdout);
	char name[100],stmp[30];
	int i,tot=0,l;
	fscanf(fin,"%s",name);
	FILE * fn=fopen(name,"r");
	while(~fscanf(fn,"%s",stmp))
		if((l=strlen(stmp))<11){
			for(i=0;i<l;++i)stmp[i]=toupper(stmp[i])-'A'+1;
			memcpy(s[l][num[l]].s,stmp,l);
			s[l][num[l]++].i=tot++;
		}
	fscanf(fin,"%d",&n);
	for(i=0;i<n;++i)fscanf(fin,"%d%d%*c%c%d",y+i,x+i,c+i,len+i),c[i]=c[i]=='A'?1:0;
	/*-------Select-------*/
	int X,Y,k,d,now,sum,MAX,maxi;
	bool flag[10][10]={0};
	for(k=0;k<n;++k){
		MAX=-1;
		for(i=k;i<n;++i){
			sum=0;
			for(X=x[i],Y=y[i],d=0;d<len[i];++d,X+=b[c[i]].x,Y+=b[c[i]].y)
				if(flag[X][Y])
					sum++;
			if(sum>MAX)MAX=sum,maxi=i;
		}
		swap(x[maxi],x[k]),swap(y[maxi],y[k]),swap(c[maxi],c[k]),swap(len[maxi],len[k]);
		for(X=x[k],Y=y[k],d=0;d<len[k];++d,X+=b[c[k]].x,Y+=b[c[k]].y)flag[X][Y]=1;
	}
	/*-------Prework-----*/
	int j;
	for(i=0;i<n;++i)
		for(j=0;j<n;++j)
			hash[i][j].next=NULL;
	S * tmp;
	memset(flag,0,sizeof(flag));
	for(i=0;i<n;++i){
		for(X=x[i],Y=y[i],d=0;d<len[i];++d,X+=b[c[i]].x,Y+=b[c[i]].y)
			if(flag[X][Y])
				p[i][d]=1;
		for(k=0;k<num[len[i]];++k){
			now=0;
			for(d=0;d<len[i];++d)
				if(p[i][d])
					now=(now*27+s[len[i]][k].s[d])%P;
			tmp=new S;
			tmp->next=hash[i][now].next;
			tmp->len=len[i];
			tmp->k=k;
			hash[i][now].next=tmp;
		}
		for(X=x[i],Y=y[i],j=0;j<len[i];++j,X+=b[c[i]].x,Y+=b[c[i]].y)flag[X][Y]=1;
	}
	/*------DFS------*/
	dfs(0);
}

总结:

①对于字符串的题,优先考虑Hash。

②对于可行性搜索,一定要调整搜索顺序,绝对可以增速,要尽量使强剪枝位于搜索树上部的位置。

③处理字符串时一定要头脑清晰,我做这道题花了5个小时的时间,被大量繁杂的循环处理弄昏了头脑。

你可能感兴趣的:(hash,DFS)