描述
小 Z 最近迷上了拼图游戏,然而智商有限,他总是无法拼出完整图案。游戏是这样的:首先小 Z 会得到一些拼图碎片,然后他试着重新排列这些碎片使得它们组成一个大小为 4 ∗ 4 的正方形。如下图。注意小 Z 不能旋转或者翻转这些碎片。
Image Lost
小 Z 得到如图 1 的碎片,然后经过重新排列得到图 2 的正方形。
由于小 Z 实在太笨了,聪明的你请写一个程序帮助他来解决这个问题吧。
格式
输入格式
输入包含多组数据,请使用 EOF。
每组数据的的第一行包含一个正整数 N,表示碎片的个数。接下来输入 N 个碎片。每个碎片的第一行是两个正整数 r 和c,表示这个碎片的行数和列数。接下来是 r 行,每一行包含 c 个字符 ’0’ 或 ’1’,’1’ 表示碎片占据这个位置,’0’ 表示该位置为空。数据保证每个碎片都是完整的一片(即 ’1’ 是相互连通的),并且没有行或者列全部为 ’0’。
输出格式
如果无法组成一个正方形,输出”No Solution”;如果有多组解,输出”Yes, many!”。否则,输出”Yes, only one!”,接下来输出一个 4 ∗ 4 的矩阵 H,H_{i,j}H
i,j
表示位置 i,j 的碎片编号。碎片编号从 1 开始。
样例1
样例输入1
4
2 3
111
101
4 2
01
01
11
01
2 1
1
1
3 2
10
10
11
4
1 4
1111
1 4
1111
1 4
1111
1 4
1111
4
1 4
1111
1 4
1111
1 4
1111
2 3
111
001
样例输出1
Yes, only one!
1112
1412
3422
3442
Yes, many!
No solution
限制
对于 30% 的数据,N < 5
对于 100% 的数据,N ≤ 16
分析:比赛的时候一脸DLX直接弃,结果发现一群人用搜索水过,表示不服,数据水也不能这样啊,结果估计只有我一个人打DLX。
关于DLX之前听说过很多次,但是都感觉是比较高深的东西,结果看了看,发现。。确实不错的东西,虽然不高深,但是很好。。有一篇文章写的很好推荐去看http://www.cnblogs.com/grenet/p/3145800.html,里面对于DLX的介绍比较详细,也很容易看懂,比赛的时候用搜索水过就算了,改题还用水法就没什么意思了。
注意行尾换行,被坑了一波。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e3+5;
const int M=N*N;
const int cs=16;
int n,m,a[N][N];
int left[M],right[M],up[M],down[M],c[M],s[M],cnt;
int ans,first[N];
inline int change(int i,int j)
{
return (i-1)*4+j;
}
inline void ins(int x,int y)
{
int cur=++cnt;
up[cur]=y,down[cur]=down[y];
up[down[y]]=cur,down[y]=cur;
if (!first[x])
{
first[x]=left[cur]=right[cur]=cur;
}
else
{
left[cur]=first[x];
right[cur]=right[first[x]];
left[right[first[x]]]=cur;
right[first[x]]=cur;
}
s[c[cur]=y]++;
}
inline void add(int x,int y,int z)
{
fo(i,0,4-x)
{
fo(j,0,4-y)
{
n++;
fo(u,1,x)
fo(v,1,y)
if (a[u][v]=='1')ins(n,change(u+i,v+j));
ins(n,z+cs);
}
}
}
inline void cover(int x)
{
left[right[x]]=left[x];
right[left[x]]=right[x];
for(int i=down[x];i!=x;i=down[i])
for(int j=right[i];j!=i;j=right[j])
up[down[j]]=up[j],down[up[j]]=down[j],s[c[j]]--;
}
inline void uncover(int x)
{
left[right[x]]=right[left[x]]=x;
for(int i=up[x];i!=x;i=up[i])
for(int j=left[i];j!=i;j=left[j])
up[down[j]]=down[up[j]]=j,s[c[j]]++;
}
vector<int>cur,way;
inline void dfs()
{
if (right[0]==0)
{
++ans;
way=cur;
return;
}
int x=right[0];
for(int i=right[x];i!=0;i=right[i])
if (s[i]<s[x])x=i;
cover(x);
for(int r=down[x];r!=x;r=down[r])
{
for(int i=right[r];i!=r;i=right[i])
cover(c[i]);
cur.push_back(r);dfs();cur.pop_back();
if (ans>1)return;
for(int i=left[r];i!=r;i=left[i])uncover(c[i]);
}
uncover(x);
}
int puzzle[4][4];
inline void print()
{
memset(puzzle,0,sizeof(puzzle));
for(vector<int>::iterator it=way.begin();it!=way.end();++it)
{
int k=*it;
for(int i=right[*it];i!=*it;i=right[i])
{
if (c[i]>cs)
{
k=i;
break;
}
}
int tmp=c[k]-cs;
for(int i=right[k];i!=k;i=right[i])
{
int x=c[i]-1;
puzzle[x/4][x%4]=tmp;
}
}
fo(i,0,3)
{
fo(j,0,3)printf("%d",puzzle[i][j]);
printf("\n");
}
}
int main()
{
freopen("puzzle.in","r",stdin);
freopen("puzzle.out","w",stdout);
while(scanf("%d",&m)!=EOF)
{
m+=cs;
n=0;
memset(first,0,sizeof(first));
fo(i,0,m)
{
s[i]=0;
right[i]=i+1;
left[i+1]=i;
up[i]=down[i]=i;
}
right[m]=0;left[0]=m;
cnt=m;
fo(i,1,m-cs)
{
int x,y;
scanf("%d%d",&x,&y);
fo(j,1,x)
fo(k,1,y)scanf(" %c",&a[j][k]);
add(x,y,i);
}
ans=0;
dfs();
if (!ans)
{
printf("No solution\n");
}
else if (ans==1)
{
printf("Yes, only one!\n");
print();
}
else
{
printf("Yes, many!\n");
}
}
return 0;
}