【洛谷P4929】【模板】舞蹈鏈(DLX)
阿新 • • 發佈:2021-01-07
題目
題目連結:https://www.luogu.com.cn/problem/P4929
給定一個\(N\)行\(M\)列的矩陣,矩陣中每個元素要麼是1,要麼是0
你需要在矩陣中挑選出若干行,使得對於矩陣的每一列\(j\),在你挑選的這些行中,有且僅有一行的第\(j\)個元素為\(1\)。
\(N,M\leq 500\),數字 \(1\) 的數量 \(\leq 5000\)。
思路
DLX 可以解決的是精確覆蓋問題。也就是任意一個位置被恰好覆蓋一次。
進行操作後為了方便刪除未被選擇的無用操作,DLX 採用交叉十字迴圈雙向鏈,使得單次刪除和重新插入的複雜度均為 \(O(1)\)。
詳解可以見 Blog。
程式碼
#include <bits/stdc++.h> using namespace std; const int N=300010; int n,m; stack<int> st; struct DLX { int tot,l[N],r[N],u[N],d[N],x[N],y[N],h[N],cnt[N]; void build(int m) { memset(h,-1,sizeof(h)); for (int i=0;i<=m;i++) l[i]=i-1,r[i]=i+1,u[i]=d[i]=i; l[0]=m; r[m]=0; tot=m; } 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 (;st.size();st.pop()) printf("%d ",st.top()); 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]) { st.push(x[i]); 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]); st.pop(); } resume(p); return 0; } }dlx; int main() { scanf("%d%d",&n,&m); dlx.build(m); for (int i=1;i<=n;i++) for (int j=1,x;j<=m;j++) { scanf("%d",&x); if (x) dlx.ins(i,j); } if (!dlx.dance()) printf("No Solution!"); return 0; }