HDU_1853 Cyclic Tour KM

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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值