原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=3488
我的链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=17728#overview
1 6 9 1 2 5 2 3 5 3 1 10 3 4 12 4 1 8 4 6 11 5 4 7 5 6 9 6 5 4
42
Source
2010 ACM-ICPC Multi-University Training Contest(6)——Host by BIT
Recommend
Zhouzeyong
题意:和hdu 1853几乎完全一样。
给你一个带权值的有向图,用若干个环覆盖所有的点,每个点只可被覆盖一次(起点
除外),求出权值和最小的环的权值。
算法:KM求完美匹配。
以城市为点建立二分图,求最小权值二分图。
如果有完美匹配,则一定存在若干包含每个顶点的环。
PS:注意初始化w[i][j]为大负数的取值,否则可能超时。
Accepted 424 KB 140 ms C++ 1943 B 2013-02-26 16:11:00 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=210; const int minn=-1000000; char map[maxn][maxn]; int w[maxn][maxn]; int lx[maxn],ly[maxn]; bool s[maxn],t[maxn]; int match[maxn]; int n; int hungary(int u)//匈牙利,匹配(找增广路) { s[u]=true;//标记入匈牙利树 for(int v=1;v<=n;v++) { if(!t[v] && lx[u]+ly[v]==w[u][v]) { t[v]=true;//标记入匈牙利树 易忘记 if(match[v]==-1 || hungary(match[v])) { match[v]=u; return true; } } } return false; } int KM() { int sum=0; memset(match,-1,sizeof(match));//初始化,易写错 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) lx[i]=-1<<30;//初始化 顶标 memset(ly,0,sizeof(ly)); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) lx[i]=max(lx[i],w[i][j]);//最大权 for(int i=1;i<=n;i++)//匹配每一个点 { while(true) { memset(s,false,sizeof(s));//每次匹配前,找增广路,初始化为0 memset(t,false,sizeof(t)); if(hungary(i)) break; else{ int a=1<<30; for(int j=1;j<=n;j++) if(s[j])//j匹配过(即在匈牙利树中) for(int k=1;k<=n;k++) if(!t[k] && a>lx[j]+ly[k]-w[j][k]) a=lx[j]+ly[k]-w[j][k]; for(int j=1;j<=n;j++)//修改顶标 { if(s[j]) lx[j]-=a; if(t[j]) ly[j]+=a; } } } } for(int i=1;i<=n;i++) sum+=w[match[i]][i]; return sum; } int main() { int test; int N,M; int u,v,length; while(scanf("%d",&test)!=EOF) { while(test--) { scanf("%d%d",&N,&M); n=N; for(int i=1;i<=N;i++) for(int j=1;j<=N;j++) w[i][j]=minn; for(int i=1;i<=M;i++) { scanf("%d%d%d",&u,&v,&length); if(-length>w[u][v]) w[u][v]=-length; } printf("%d\n",-KM()); } } return 0; }