【UVA1309】Sudoku(DLX)

点此看题面

大致题意: 让你填完整一个\(16*16\)的数独。

解题思路

我们知道,数独问题显然可以用\(DLX\)解决。

考虑对于一个数独,它要满足的要求为:每个位置都必须有数每一行都必须有全部\(16\)个数每一列都必须有全部\(16\)个数每一个\(16\)宫格都必须有全部\(16\)个数

我们定义一个状态\((i,j,k)\),表示在第\(i\)行第\(j\)列填\(k\)

对于一种填法它可以同时满足\(4\)种要求中各一种,因此如果把一种填法看作\(DLX\)中的一行,则每一行有\(4\)\(1\)

注意,对于已经给出数的位置,我们只能选择给出的那种填法, 否则, 可以选择任意数,有\(16\)种填法。

而对于这\(4\)种限制,每种都要求有\(256\)个,因此\(DLX\)共有\(1024\)列。

然后就可以按此跑\(DLX\)板子了。

具体实现详见代码。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
using namespace std;
char a[20][20];struct Operate {int x,y,v;}p[(1<<12)+5];
class DancingLinksX//DLX模板
{
    private:
        int tot,sz[(1<<10)+5],lnk[(1<<12)+5],res[(1<<8)+5];
        struct node
        {
            int x,y,u,d,l,r;
            I node(CI X=0,CI Y=0,CI U=0,CI D=0,CI L=0,CI R=0):x(X),y(Y),u(U),d(D),l(L),r(R){}
        }O[(1<<14)+5];
        I bool Dance(CI x)
        {
            #define Delete(x)\
            {\
                O[O[O[x].l].r=O[x].r].l=O[x].l;\
                for(RI i=O[x].d;i^x;i=O[i].d) for(RI j=O[i].r;j^i;j=O[j].r)\
                    O[O[O[j].u].d=O[j].d].u=O[j].u,--sz[O[j].y];\
            }
            #define Regain(x)\
            {\
                for(RI i=O[x].d;i^x;i=O[i].d) for(RI j=O[i].r;j^i;j=O[j].r)\
                    O[O[j].u].d=O[O[j].d].u=j,++sz[O[j].y];\
                O[O[x].l].r=O[O[x].r].l=x;\
            }
            if(!O[0].r)
            {
                RI i;for(i=1;i^x;++i) a[p[res[i]].x][p[res[i]].y]=p[res[i]].v;//更新到数独上
                for(i=1;i<=16;++i) puts(a[i]+1);return 1;//输出
            }
            RI i,j,t=O[0].r;for(i=O[t].r;i;i=O[i].r) sz[t]>sz[i]&&(t=i);
            Delete(t);for(i=O[t].d;i^t;i=O[i].d)
            {
                for(res[x]=O[i].x,j=O[i].r;j^i;j=O[j].r) Delete(O[j].y);
                if(Dance(x+1)) return 1;
                for(j=O[i].l;j^i;j=O[j].l) Regain(O[j].y);
            }Regain(t);return 0;
        }
    public:
        I void Init(CI x)
        {
            RI i;for(tot=x,i=0;i<=x;++i) O[i]=node(0,i,i,i,i-1,i+1);
            O[O[0].l=x].r=0,memset(lnk,-1,sizeof(lnk)),memset(sz,0,sizeof(sz));
        }
        I void Insert(CI x,CI y) 
        {
            ++sz[y],O[++tot]=node(x,y,y,O[y].d),O[y].d=O[O[y].d].u=tot, 
            ~lnk[x]?(O[tot].l=lnk[x],O[tot].r=O[lnk[x]].r,O[lnk[x]].r=O[O[lnk[x]].r].l=tot) 
            :(lnk[x]=O[tot].l=O[tot].r=tot); 
        }
        I void Solve() {Dance(1);}
}DLX;
int main()
{
    RI Ttot,i,j,k,cnt,t=0;W(~scanf("%s",a[1]+1))
    {
        #define P(x,y) ((x-1<<4)+y)
        #define T(x,y) (((x-1>>2)<<2)+(y+3>>2))
        t++&&putchar('\n');//注意输出空行
        for(DLX.Init(1<<10),cnt=0,i=2;i<=16;++i) scanf("%s",a[i]+1);
        for(i=1;i<=16;++i) for(j=1;j<=16;++j) for(k=1;k<=16;++k)
        {
            if(a[i][j]^'-'&&(a[i][j]&31)^k) continue;//对于已经给定的数,必须按这种填法填
            p[++cnt].x=i,p[cnt].y=j,p[cnt].v=64|k,//存下这种填法
            DLX.Insert(cnt,P(i,j)),DLX.Insert(cnt,P(i,k)+256),//每一位要有数、每一行要有全部数
            DLX.Insert(cnt,P(j,k)+512),DLX.Insert(cnt,P(T(i,j),k)+768);//每一列要有全部数、每一16宫格要有全部数
        }DLX.Solve();
    }return 0;
}

转载于:https://www.cnblogs.com/chenxiaoran666/p/UVA1309.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值