[BZOJ 3571] 画框
Link:
Solution:
和 BZOJ2395 的建模完全相同,(BZOJ2395 题解传送门)
仅仅是将其中的基础问题由最小生成树改成了二分图最大完美匹配
只要将原来的Kruscal模块改为KM算法即可
Code:
//by NewErA #include <bits/stdc++.h> using namespace std; typedef long long ll; const int INF=1<<27; struct Vector { int x,y; Vector(const int &A,const int &B){x=A,y=B;}Vector(){} }; Vector operator - (const Vector &a,const Vector &b){return Vector(a.x-b.x,a.y-b.y);} int Cross(const Vector &a,const Vector &b){return a.x*b.y-a.y*b.x;} const int MAXN=100; int T,n,G[MAXN][MAXN],dat1[MAXN][MAXN],dat2[MAXN][MAXN]; Vector mina,minb,ans; ll res; int match[MAXN*2],slack[MAXN*2],lx[MAXN],ly[MAXN]; bool visx[MAXN],visy[MAXN]; bool dfs(int u) { visx[u]=true; for(int i=1;i<=n;i++) { if(visy[i]) continue; int d=lx[u]+ly[i]-G[u][i]; if(!d) { visy[i]=true; if(match[i]==-1 || dfs(match[i])) { match[i]=u; return true; } } else slack[i]=min(d,slack[i]); } return false; } Vector KM() //$O(n^3)$的KM做法 { memset(match,-1,sizeof(match)); fill(lx,lx+MAXN,-INF);memset(ly,0,sizeof(ly)); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) lx[i]=max(lx[i],G[i][j]); for(int i=1;i<=n;i++) { fill(slack,slack+MAXN,INF); while(true) { memset(visx,0,sizeof(visx)); memset(visy,0,sizeof(visy)); if(dfs(i)) break; else { int d=INF; for(int i=1;i<=n;i++) if(!visy[i]) d=min(d,slack[i]); for(int i=1;i<=n;i++) { if(visx[i]) lx[i]-=d; if(visy[i]) ly[i]+=d; else slack[i]-=d; } } } } Vector cur=Vector(0,0); for(int i=1;i<=n;i++) cur.x+=dat1[match[i]][i],cur.y+=dat2[match[i]][i]; if(res>(ll)cur.x*(ll)cur.y) res=(ll)cur.x*(ll)cur.y; //注意开long long return cur; } void Solve(Vector A,Vector B) { for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) G[i][j]=-(dat1[i][j]*(A.y-B.y)+dat2[i][j]*(B.x-A.x)); //修改权值 Vector C=KM(); if(Cross(B-A,C-A)>=0) return; Solve(A,C);Solve(C,B); } int main() { cin >> T; while(T--) { cin >> n;res=40000*40000; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin >> dat1[i][j],G[i][j]=-dat1[i][j]; //最小匹配转为最大匹配,权值由正变为负即可 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin >> dat2[i][j]; mina=KM(); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) G[i][j]=-dat2[i][j]; minb=KM(); Solve(mina,minb); cout << res << endl; } return 0; }
Review:
1、需要注意的就是最小匹配转为最大匹配的手法:
将原来的正权值求最小变为负权值求最大
2、千万不能再忘开long long 了!