poj 1853 Cyclic Tour(二分圖拆點+KM演算法)
阿新 • • 發佈:2018-11-16
題目連結:hdu 1853
題意:給出n個點,m條邊的帶權有向圖,讓你分成幾個迴圈走完全部點,求最小路徑。
參考部落格:https://blog.csdn.net/u013480600/article/details/38760767
把頂點i拆分成 i和i',
假設原圖的有向環為(1->2->3->1) and(6->5->4->6),那麼二分圖的完備匹配就是1->2’ 2->3’ 3->1’6->5’ 5->4’ 4->6’
假設二分圖的完備匹配是1->2’ 2->3’ 3->1’ 6->5’ 5->4’ 4->6’ 那麼原圖的有向環為(1->2->3->1) and (6->5->4->6))
///二分圖拆點,KM演算法 #include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define INF 0x3f3f3f3f const int maxn=110; int G[maxn][maxn],match[maxn],lx[maxn],ly[maxn],slack[maxn]; int nx,ny; bool visx[maxn],visy[maxn]; bool findpath(int x) { int tempdelta; visx[x]=1; for(int y=0;y<ny;y++) { if(visy[y]) continue; tempdelta=lx[x]+ly[y]-G[x][y]; if(tempdelta==0){ visy[y]=1; if(match[y]==-1||findpath(match[y])){ match[y]=x; return 1; } } else if(slack[y]>tempdelta) slack[y]=tempdelta; } return 0; } void KM() { for(int x=0;x<nx;x++) { for(int j=0;j<ny;j++) slack[j]=INF; while(1) { memset(visx,0,sizeof(visx)); memset(visy,0,sizeof(visy)); if(findpath(x)) break; int delta=INF; for(int j=0;j<ny;j++) if(!visy[j]) delta=min(delta,slack[j]); for(int i=0;i<nx;i++) if(visx[i]) lx[i]-=delta; for(int j=0;j<ny;j++) { if(visy[j]) ly[j]+=delta; else slack[j]-=delta; } } } } void solve() { memset(match,-1,sizeof(match)); memset(ly,0,sizeof(ly)); for(int i=0;i<nx;i++) { lx[i]=-INF; for(int j=0;j<ny;j++) if(lx[i]<G[i][j]) lx[i]=G[i][j]; } KM(); } int main() { int n,m; while(~scanf("%d%d",&n,&m)) { nx=ny=n; for(int i=0;i<n;i++) for(int j=0;j<n;j++) G[i][j]=-INF; while(m--) { int x,y,weight; scanf("%d%d%d",&x,&y,&weight);///建圖,拆點 if(-weight>G[x-1][y-1]) G[x-1][y-1]=-weight; } solve(); int ans=0; int num=0; for(int j=0;j<ny;j++) { ///此處的匹配一定不會為-1,因為把-INF當作權值了,故再加個判斷就行了 if(match[j]==-1||G[match[j]][j]==-INF){ puts("-1"); break; } ans+=G[match[j]][j]; ++num; } if(num==ny) printf("%d\n",-ans); } return 0; }