HDU3488 Tour KM
原文链接http://www.cnblogs.com/zhouzhendong/p/8284304.html
题目传送门 - HDU3488
题意概括
给一个n的点m条边的有向图。
然后让你把这个图分成许多环,问环中边权和最小为多少。
题目保证一定存在合法的方案。
题解
我们把每一个点扯成两个点。
一个专门接受入度,一个专门接受出度,然后就是KM裸题了。
数组模拟链表可能会比邻接矩阵快一些。
代码
#include <cstring> #include <algorithm> #include <cstdlib> #include <cmath> #include <cstdio> using namespace std; const int INF=1e9+7; const int N=205,M=30005; struct Gragh{ int cnt,y[M],z[M],nxt[M],fst[N]; void clear(){ cnt=0; memset(fst,0,sizeof fst); } void add(int a,int b,int c){ y[++cnt]=b,z[cnt]=c,nxt[cnt]=fst[a],fst[a]=cnt; } }g; int T,n,m,match[N],ex[N],ey[N],minadd[N]; bool visx[N],visy[N]; bool Match(int x){ visx[x]=1; for (int i=g.fst[x];i;i=g.nxt[i]){ int y=g.y[i]; if (visy[y]) continue; int add=ex[x]+ey[y]-g.z[i]; if (!add){ visy[y]=1; if (!match[y]||Match(match[y])){ match[y]=x; return 1; } } else minadd[y]=min(minadd[y],add); } return 0; } int KM(){ memset(match,0,sizeof match); memset(ey,0,sizeof ey); for (int i=1;i<=n;i++){ ex[i]=-INF; for (int j=g.fst[i];j;j=g.nxt[j]) ex[i]=max(ex[i],g.z[j]); } for (int i=1;i<=n;i++){ for (int j=1;j<=n;j++) minadd[j]=INF; while (1){ memset(visx,0,sizeof visx); memset(visy,0,sizeof visy); if (Match(i)) break; int d=INF; for (int j=1;j<=n;j++) if (!visy[j]) d=min(d,minadd[j]); for (int j=1;j<=n;j++){ if (visx[j]) ex[j]-=d; if (visy[j]) ey[j]+=d; else minadd[j]-=d; } } } int ans=0; for (int i=1;i<=n;i++){ int Max=-INF; for (int j=g.fst[match[i]];j;j=g.nxt[j]) if (g.y[j]==i) Max=max(Max,g.z[j]); ans+=Max; } return ans; } int main(){ scanf("%d",&T); while (T--){ scanf("%d%d",&n,&m); g.clear(); for (int i=1,a,b,c;i<=m;i++){ scanf("%d%d%d",&a,&b,&c); g.add(a,b,-c); } printf("%d\n",-KM()); } return 0; }