【TC10738】TheContest【Hall 定理】【贪心】【二分图匹配】
題意:給 n×mn\times mn×m 的表格填入 [1,max?(n,m)][1,\max(n,m)][1,max(n,m)] 的數,每行每列不能重復,且字典序最小。
n,m≤50n,m\leq 50n,m≤50
數據范圍很小,所以是多項式就能過。
考慮每個位置從小到大依次填值,判斷之后是否有合法解。
當 n≤mn\leq mn≤m 時所有數和沒用過的列做二分圖匹配就可以了。但 n>mn>mn>m 時就變成了三分圖匹配,沒法做。
然后是個結論:當 n>mn>mn>m 時,填了 xxx 行,問題有解當且僅當每個數還需要填的個數不超過剩余行數。顯然每個數必須恰好填 nnn 個,所以這個“需要填的個數”是確定的。
必要性顯然,充分性后述。
考慮每一行,有一些數是這一列必須匹配的,其余數可以匹配也可以不匹配。可以用匈牙利限制順序或上下界網絡流解決。
現在證明一定存在滿匹配。考慮 Hall 定理,左邊是所有數,右邊是所有列,有連邊當且僅當這個數沒在這列出現過。我們任意選擇 iii 列,有滿匹配的充要條件是這 iii 列對應的點的鄰邊的交集大小 ≥k\geq k≥k。假設交集小于 kkk,那么一定有一種數在后面出現了 >ixi=x>\dfrac{ix}{i}=x>iix?=x 次,其中 xxx 為剩余行數,與已知矛盾,故得證。
然后直接跑就行了。復雜度 O(poly(n,m))\Omicron(poly(n,m))O(poly(n,m))
代碼的題不太一樣,僅供參考
#include <iostream> #include <cstdio> #include <cstring> #include <cctype> #include <queue> #define MAXN 65 #define MAXM 2077 using namespace std; const int INF=0x7fffffff; struct edge{int u,v,c;}e[MAXM]; int head[MAXN],cur[MAXN],nxt[MAXM],cnt=1; inline void insert(int u,int v,int c){e[++cnt]=(edge){u,v,c};nxt[cnt]=head[u];head[u]=cnt;} inline void addnode(int u,int v,int c){insert(u,v,c),insert(v,u,0);} int dis[MAXN],S,T,SS,TT; bool bfs(int S,int T) {queue<int> q;q.push(T);memset(dis,-1,sizeof(dis));dis[T]=0;while (!q.empty()){int u=q.front();q.pop();for (int i=head[u];i;i=nxt[i])if (e[i^1].c&&dis[e[i].v]==-1){dis[e[i].v]=dis[u]+1;q.push(e[i].v);if (e[i].v==S) return true;}}return false; } int dfs(int u,int f,int T) {if (u==T||!f) return f;int used=0;for (int& i=cur[u];i;i=nxt[i])if (e[i].c&&dis[u]==dis[e[i].v]+1){int w=dfs(e[i].v,min(e[i].c,f),T);e[i^1].c+=w,e[i].c-=w;used+=w,f-=w;if (!f) break;}if (!used) dis[u]=-1;return used; } inline int dinic(int S,int T){int ans=0;while (bfs(S,T)) memcpy(cur,head,sizeof(cur)),ans+=dfs(S,INF,T);return ans;} inline void clear(){memset(head,0,sizeof(head));for (int i=2;i<=cnt;i++) nxt[i]=0;cnt=1;} int a[MAXN][MAXN],vis[MAXN][MAXN],now[MAXN],res[MAXN]; int n,m,lim,vip[MAXN],tot; bool check(int y) {clear();int sum=0;for (int i=1;i<=lim;i++) if (!now[i]) {if (vip[i]) addnode(SS,i,1),++sum;else addnode(S,i,1);}addnode(T,S,INF),addnode(S,TT,sum);for (int i=y+1;i<=m;i++) addnode(i+lim,T,1);for (int i=1;i<=lim;i++)if (!now[i])for (int j=y+1;j<=m;j++)if (!vis[j][i])addnode(i,j+lim,1);if (dinic(SS,TT)<sum) return false;return dinic(S,T)==m-y; } int main() {for (n=1;n<=30;n++)for (m=1;m<=30;m++){lim=max(n,m);S=2*lim+1,T=S+1,SS=T+1,TT=SS+1;for (int i=1;i<=lim;i++) res[i]=n+m-lim;for (int i=1;i<=n;i++){for (int j=1;j<=lim;j++) vip[j]=(res[j]==n-i+1); for (int j=1;j<=m;j++)while (++a[i][j]){if (now[a[i][j]]||vis[j][a[i][j]]) continue;now[a[i][j]]=1,vis[j][a[i][j]]=1;if (check(j)) break;now[a[i][j]]=0,vis[j][a[i][j]]=0;}for (int j=1;j<=m;j++) --res[a[i][j]];memset(now,0,sizeof(now)); }for (int i=1;i<=n;i++,puts(""))for (int j=1;j<=m;j++)printf("%d ",a[i][j]);puts("");memset(a,0,sizeof(a)),memset(vis,0,sizeof(vis)); }return 0; }總結
以上是生活随笔為你收集整理的【TC10738】TheContest【Hall 定理】【贪心】【二分图匹配】的全部內容,希望文章能夠幫你解決所遇到的問題。