http://acm.hdu.edu.cn/showproblem.php?pid=1853
题意:
有N个城镇和 M条边,每条边都有一个权值,要求用若干个环把所有的顶点覆盖,
而且每个顶点要求只被一个环覆盖,求一种权值和最小的方案。
思路:
我们先分析每个环的情况,因为每个顶点只在一个环中,因此我们可以看出,对
于每个环中的结点,入度和出度都为1,而且是每个点都必须要有一个入度和一个
出度。问题到这里就可以转化为,选择一种结点之间的组合,所有选择的边的权
值最小。我们可以将一个结点拆成两个结点,然后将两部分结点分开,一部分考虑
它的出度,一部分考虑入度,这样就转化成了二分图的最小权最优完备匹配,我们可以
利用KM算法,但是KM算法求的是最大权最优完备匹配,我们只需要将所有边的权值
w[i][j] = -w[i][j]即可。值得注意的是,图中存在重边。
代码:
#include<stdio.h>
#include<string.h>
#define CC(m,what) memset(m,what, sizeof(m))
#define FF(i, CH) for(int i=0;i<CH;i++)
const int inf = (1<<30) ;
int N, M ;
const int MAXN = 110 ;
int w[MAXN][MAXN] ;
int lx[MAXN] , ly[MAXN] ;
int match[MAXN] , link[MAXN] ;
bool sx[MAXN] ,sy[MAXN] ;
bool dfs(int u){
sx[u] = 1 ;
FF(v, N){
if( sy[v] ) continue ;
int t = lx[u] + ly[v] - w[u][v] ;
if(t == 0){
sy[v] =1 ;
if(match[v]==-1 || dfs( match[v] ) ){
match[v] = u ;
return true ;
}
}
else{
if( link[v] > t)
link[v] = t ;
}
}
return false ;
}
void KM(){
CC(match , -1) ;
CC(ly, 0) ;
FF(i , N){
lx[i] = -inf ;
FF(j , N){
if( lx[i] < w[i][j] )
lx[i] = w[i][j] ;
}
}
FF(i , N){
FF(j , N){
link[j] = inf ;
}
while(1){
CC(sx, 0) ; CC(sy , 0) ;
if( dfs(i) ) break ;
int d = inf ;
FF(j , N){
if(!sy[j] && d>link[j]){
d = link[j] ;
}
}
FF(j , N){
if( sx[j] )
lx[j] -= d;
}
FF(j , N){
if( sy[j] ){
ly[j] += d;
}
else
link[j] -= d;
}
}
}
int res = 0 ;
FF(i , N){
if( match[i]==-1 || w[ match[i] ][i]==-inf){
printf("-1\n") ; return ;
}
res += w[match[i]][i] ;
}
printf("%d\n",-res);
}
int main(){
int a, b ,c ;
while(scanf("%d%d",&N,&M) == 2){
FF(i ,N){
FF(j , N)
w[i][j] = -inf ;
}
FF(i , M){
scanf("%d%d%d",&a,&b,&c);
a-- ; b --;
if( w[a][b] < -c ){
w[a][b] = -c ;
}
}
KM() ;
}
return 0;
}