【XJOI2899】數獨
阿新 • • 發佈:2021-01-07
題目
題目連結: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; }