【TJOI2014】拼图(puzzle) dancing links X

描述

小 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值