[费用流] HDU1853 Cyclic Tour

Cyclic Tour

题意:
给一个带权有向图,要求找出若干个环,每个点必须唯一包含在一个环内,要求所有环上边权值总和最小,求权值总和,无解输出-1。
题解:
每个点必须包含在一个环内,换句话说就是任意点的出度=入度=1。
于是拆点,左点表示出,右点表示入,跑出费用流,判断是不是所有点都是出度等于入度等于1。

#include<bits/stdc++.h>
using namespace std;
const int INF = ~0u>>2;
const int M = 10005;
const int N = 405;
struct eg{
    int u, v, cap, cost;
    eg(){}
    eg(int a, int b, int c, int d){ u = a, v = b, cap = c, cost = d; };
}edg[M<<2];
int fir[M], nex[M<<2];
int n, m, s, t, ecnt;

void add(int a, int b, int c, int d){
    edg[ecnt] = eg(a, b, c, d);
    nex[ecnt] = fir[a], fir[a] = ecnt++;
    edg[ecnt] = eg(b, a, 0, -d);
    nex[ecnt] = fir[b], fir[b] = ecnt++;
}

int pre[M], dis[N];
int spfa(int s, int t, int n){
    queue<int>q;
    bool vis[N]={0};
    memset(pre, -1, sizeof(pre));
    for(int i = 0; i <= t; ++i) dis[i] = INF;
    dis[s] = 0; vis[s] = 1;
    q.push(s);
    while( !q.empty() ){
        int u = q.front(); q.pop(); vis[u] = 0;
        for(int k = fir[u]; k != -1; k = nex[k]){
            int v = edg[k].v;
            if( edg[k].cap && dis[u] + edg[k].cost < dis[v]){
                dis[v] = edg[k].cost + dis[u];
                pre[v] = k;
                if(!vis[v]){
                    q.push(v);
                    vis[v] = 1;
                }
            }
        }
    }
    return dis[t] != INF;
}
int mincostEK(int s, int t, int n){
    int res = 0, minflow, maxflow = 0;
    while( spfa(s, t, n)){
        minflow = INF;
        for(int k = pre[t]; k != -1; k = pre[edg[k].u]){
            minflow = min(minflow, edg[k].cap);
        }
        for(int k = pre[t]; k != -1; k = pre[edg[k].u]){
            edg[k].cap -= minflow;
            edg[k^1].cap += minflow; 
        }
        res += dis[t] * minflow;
        maxflow += minflow;
    }
    if(maxflow*2+2 != n) return -1;
    return res;
}
int main(){
    int a, b, c;
    while(scanf("%d%d", &n, &m) != EOF){
        ecnt = 0;
        memset(fir, -1, sizeof(fir));
        s = 0, t = n*2+1;
        for(int i = 1; i <= m; ++i){
            scanf("%d%d%d", &a, &b, &c);
            add(a, b+n, 1, c);
        }
        for(int i = 1; i <= n; ++i) add(s, i, 1, 0);
        for(int i = n+1; i <= 2*n; ++i) add(i, t, 1, 0);
        printf("%d\n", mincostEK(s, t, n*2+2));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值