题意:
给你一个N个点M条边的带权有向图,现在要你求这样一个值:该有向图中的所有顶点正好被1个或多个不相交的有向环覆盖.这个值就是 所有这些有向环的权值和. 要求该值越小越好.
SOL:
本来还想tarjan什么的,就是不能往二分图上靠。。。然后看了二分图的建图觉得非常神奇。我们可以把一个点O拆成两个点O和O‘,发现一个点i如果能到点j,那么我们就将i与j’建一条边,然后构成整张图后S与S‘分别为两个集合来跑KM。正确性现在还有点晕,先挖个坑
领悟到建图的重要性,做得题太少还是太年轻。
#include<cstdio> #include<cstring> #include<algorithm> #define INF 1e9 using namespace std; const int maxn=100+10; struct Max_Match { int n,W[maxn][maxn]; int Lx[maxn],Ly[maxn]; bool S[maxn],T[maxn]; int left[maxn]; bool match(int i) { S[i]=true; for(int j=1;j<=n;j++)if(Lx[i]+Ly[j]==W[i][j] && !T[j]) { T[j]=true; if(left[j]==-1 || match(left[j])) { left[j]=i; return true; } } return false; } void update() { int a=1<<30; for(int i=1;i<=n;i++)if(S[i]) for(int j=1;j<=n;j++)if(!T[j]) a=min(a, Lx[i]+Ly[j]-W[i][j]); for(int i=1;i<=n;i++) { if(S[i]) Lx[i] -=a; if(T[i]) Ly[i] +=a; } } int solve(int n) { this->n=n; memset(left,-1,sizeof(left)); for(int i=1;i<=n;i++) { Lx[i]=Ly[i]=0; for(int j=1;j<=n;j++) Lx[i]=max(Lx[i], W[i][j]); } for(int i=1;i<=n;i++) { while(true) { for(int j=1;j<=n;j++) S[j]=T[j]=false; if(match(i)) break; else update(); } } int ans=0; for(int i=1;i<=n;i++) { if(W[left[i]][i]==-INF) return -1; ans += W[left[i]][i]; } return -ans; } }KM; int main() { int n,m; while(scanf("%d%d",&n,&m)==2) { for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) KM.W[i][j]=-INF; while(m--) { int u,v,w; scanf("%d%d%d",&u,&v,&w); KM.W[u][v]=max(KM.W[u][v],-w); } printf("%d\n",KM.solve(n)); } return 0; }
原文:http://www.cnblogs.com/YCuangWhen/p/5200125.html