下面是一个简单的例子。在某一行中,线索给出了“2 1 4”三个数字,那么从这一行的某个位置起将依次出现两个相邻的黑色方格、若干白色方格、一个黑色方格、若干白色方格、四个连续的黑色方格,并且不再有其它的黑色方格。一种可能的情况如下图所示。
例如,下图给出了某个谜题的一个可能的解(第二列没有数字表示这一列没有黑色方格)。
对于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;
}