题目来源:http://showproblem.php?pid=3488
题意
有一个有向图,图中包括一个或多个环,那么使用一个或者多个环的情况下将所有点进行覆盖,最小权值是多少。环:只有一个点经过两次,其余各点各经过一次
思路
Kuhn-Munkers算法的几种变形应用
1.Kuhn-Munkers算法是求最大权完备匹配,如果要求最小权完备匹配怎么办?方法很简单,只需将所有的边权值取其相反数,求最大权完备匹配,匹配的值再取相反数即可。
2.Kuhn-Munkers算法的运行要求是必须存在一个完备匹配,如果求一个最大权匹配(不一定完备)该如何办?依然很简单,把不存在的边权值赋为0。
3.Kuhn-Munkers算法求得的最大权匹配是边权值和最大,如果我想要边权之积最大,又怎样转化?还是不难办到,每条边权取自然对数,然后求最大和权匹配,求得的结果a再算出e^a就是最大积匹配。
以上文字来源:http://dsqiu.iteye.com/blog/1689505
目测这道题属于第一种变种。。。。
so 。。。模板躺过 。。。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=200+19;
const int INF=0x3f3f3f3f;
int link[maxn],v_x[maxn],v_y[maxn];
int mp[maxn][maxn],slack[maxn],ex_x[maxn],ex_y[maxn];
int n,w;
void init()
{
scanf("%d%d",&n,&w);
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
mp[i][j]=-INF;
for(int i=0; i<w; i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
if(mp[x][y]<-z)
mp[x][y]=-z;
}
}
bool dfs(int i)
{
v_x[i]=1;
for(int j=1; j<=n; j++)
{
if(!v_y[j])
{
int dx=ex_x[i]+ex_y[j]-mp[i][j];
if(dx==0)
{
v_y[j]=1;
if(link[j]==-1||dfs(link[j]))
{
link[j]=i;
return true;
}
}
else
slack[j]=min(slack[j],dx);
}
}
return false;
}
void solve()
{
memset(link,-1,sizeof(link));
memset(ex_y,0,sizeof(ex_y));
for(int i=1; i<=n; i++)
{
ex_x[i]=-INF;//纠结了很久。。。
for(int j=1; j<=n; j++)
if(ex_x[i]<mp[i][j])
ex_x[i]=mp[i][j];
}
for(int i=1; i<=n; i++)
{
memset(slack,INF,sizeof(slack));
while(1)
{
memset(v_x,0,sizeof(v_x));
memset(v_y,0,sizeof(v_y));
if(dfs(i)) break;
int d=INF;
for(int j=1; j<=n; j++)
{
if(!v_y[j])
d=d>slack[j]?slack[j]:d;
}
for(int j=1; j<=n; j++)
{
if(v_x[j]) ex_x[j]-=d;
if(v_y[j]) ex_y[j]+=d;
else slack[j]-=d;//因为上一句。。。
}
}
}
int sum=0;
for(int i=1; i<=n; i++)
sum+=mp[link[i]][i];
printf("%d\n",-sum);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
init();
solve();
}
}