1. 程式人生 > 實用技巧 >【XJOI2899】數獨

【XJOI2899】數獨

題目

題目連結:https://dev.xjoi.net/contest/1646/problem/3
求一個 \(16\times 16\) 的數獨的唯一解。

思路

DLX。
\((x,y,v)\) 表示在位置 \((x,y)\) 填入字元 \(v\) 的貢獻。那麼最多有 \(16^3=4096\) 行。
然後如果一個位置 \((x,y)\) 是空的,那麼它會對應四列:\((x,y)\) 必須填數,第 \(x\) 行存在字元 \(v\),第 \(y\) 列存在字元 \(v\),該十六宮格存在字元 \(v\)。所有最多有 \(4\times 16^2=1024\) 列。
然後就轉化了一個最小覆蓋問題。\(4029\times 1024\)

可以接受,而且這是一個十分輕鬆的上界。

程式碼

#include <bits/stdc++.h>
#define ID(x,y) (((x-1)/4)*4+(y-1)/4+1)
using namespace std;
 
const int N=5000010;
int row,id[4][20][20],qwq[N][3];
char a[20][20];
bool vis[20];
 
struct DLX
{
    int tot,l[N],r[N],u[N],d[N],h[N],x[N],y[N],cnt[N];
     
    void build()
    {
        memset(h,-1,sizeof(h));
        for (int i=0;i<=tot;i++)
            l[i]=i-1,r[i]=i+1,u[i]=d[i]=i;
        l[0]=tot; r[tot]=0;
    }
     
    void ins(int i,int j)
    {
        int p=++tot;
        x[p]=i; y[p]=j; cnt[j]++;
        u[p]=u[j]; d[u[j]]=p; d[p]=j; u[j]=p;
        if (h[i]==-1)
            h[i]=p,l[p]=r[p]=p;
        else
            l[p]=l[h[i]],r[l[p]]=p,r[p]=h[i],l[h[i]]=p;
    }
     
    void remove(int k)
    {
        r[l[k]]=r[k]; l[r[k]]=l[k];
        for (int i=d[k];i!=k;i=d[i])
            for (int j=l[i];j!=i;j=l[j])
                d[u[j]]=d[j],u[d[j]]=u[j],cnt[y[j]]--;
    }
     
    void resume(int k)
    {
        for (int i=u[k];i!=k;i=u[i])
            for (int j=r[i];j!=i;j=r[j])
                d[u[j]]=j,u[d[j]]=j,cnt[y[j]]++;
        r[l[k]]=k; l[r[k]]=k;
    }
     
    bool dance()
    {
        if (!r[0])
        {
            for (int i=1;i<=16;i++)
            {
                for (int j=1;j<=16;j++)
                    putchar(a[i][j]);
                putchar(10);
            }
            return 1;
        }
        int p=0;
        for (int i=r[0];i;i=r[i])
            if (cnt[i]<cnt[p] || !p) p=i;
        if (!p) return 0;
        remove(p);
        for (int i=d[p];i!=p;i=d[i])
        {
            a[qwq[x[i]][0]][qwq[x[i]][1]]=qwq[x[i]][2]+'A'-1;
            for (int j=l[i];j!=i;j=l[j]) remove(y[j]);
            if (dance()) return 1;
            for (int j=r[i];j!=i;j=r[j]) resume(y[j]);
        }
        resume(p);
        return 0;
    }
}dlx;
 
int main()
{
    for (int i=1;i<=16;i++)
    {
        scanf("%s",a[i]+1);
        for (int j=1;j<=16;j++)
            if (a[i][j]=='-') id[0][i][j]=++dlx.tot;
    }
    for (int i=1;i<=16;i++)
    {
        memset(vis,0,sizeof(vis));
        for (int j=1;j<=16;j++)
            if (a[i][j]!='-') vis[a[i][j]-'A'+1]=1;
        for (int j=1;j<=16;j++)
            if (!vis[j]) id[1][i][j]=++dlx.tot;
    }
    for (int i=1;i<=16;i++)
    {
        memset(vis,0,sizeof(vis));
        for (int j=1;j<=16;j++)
            if (a[j][i]!='-') vis[a[j][i]-'A'+1]=1;
        for (int j=1;j<=16;j++)
            if (!vis[j]) id[2][i][j]=++dlx.tot;
    }
    for (int i=1;i<=16;i++)
    {
        memset(vis,0,sizeof(vis));
        for (int j=((i-1)/4)*4+1;j<=((i-1)/4)*4+4;j++)
            for (int k=((i-1)%4)*4+1;k<=((i-1)%4)*4+4;k++)
                if (a[j][k]!='-') vis[a[j][k]-'A'+1]=1;
        for (int j=1;j<=16;j++)
            if (!vis[j]) id[3][i][j]=++dlx.tot;
    }
    dlx.build();
    for (int i=1;i<=16;i++)
        for (int j=1;j<=16;j++)
            if (a[i][j]=='-')
                for (int k=1;k<=16;k++)
                    if (id[1][i][k] && id[2][j][k] && id[3][ID(i,j)][k])
                    {
                        row++;
                        dlx.ins(row,id[0][i][j]);
                        dlx.ins(row,id[1][i][k]);
                        dlx.ins(row,id[2][j][k]);
                        dlx.ins(row,id[3][ID(i,j)][k]);
                        qwq[row][0]=i; qwq[row][1]=j; qwq[row][2]=k;
                    }
    dlx.dance();
    return 0;
}