【网络流】hdu1853 Cyclic Tour

题意:在有向图中找若干个环,使得构成这些环的边的权值最小,且满足每个点在这些环中出现且只出现一次,每个环中至少有两个点
难度:2
题解:二分图最优匹配。把每一个点拆成两点Xi和Yi,新增一个附加源s和附加汇t,s向X集合中的所有点连一条容量为1,费用为0的边,Y集合中的所有点向t连一条容量为1,费用为0的边;对于给出的各条边(i,j,w),从Xi向Yj连一条容量不为0,费用为w的边。然后求最小费用最大流,如果最大流为节点数的话,最小费用就是答案,因为对于X[]集合可假设为各点的出度,Y[]集合可以假设为各点的入度,答案能够保证每个点的出度和入度都为1。
#include <cstdio>
#include <cstring> 
#include <iostream>
#include <algorithm>
using namespace std;
#define inf (1<<29)
const int maxn = 1001 , maxm = 44444;
struct Edge {
    int u,v,f,c,next;    
}edge[maxm];
int E,head[maxn];
void init() {
    E=0;memset(head,-1,sizeof(head));    
}
void _add(int u,int v,int f,int c) {
    edge[E].u=u;edge[E].v=v;edge[E].f=f;edge[E].c=c;edge[E].next=head[u];head[u]=E++;   
}
void addedge(int u,int v,int f,int c) {
    _add(u,v,f,c); _add(v,u,0,-c);    
}
int dist[maxn],p[maxn],sta[maxn];
bool vis[maxn];
int n,maxf,minc;
bool spfa(int s,int t) {
    int u,v,f,c;
    for(int i=0;i<n;i++) dist[i] = inf , vis[i] = 0 , p[i] = -1;
    int top = 0;
    sta[++top] = s;
    dist[s] = 0;
    while(top) {
        u = sta[top--];
        vis[u] = 0;
        for(int i=head[u];i!=-1;i=edge[i].next) {
            v=edge[i].v;
            f=edge[i].f;
            c=edge[i].c;
            if(f && dist[v] > dist[u] + c) {
                dist[v] = dist[u] + c;
                p[v] = i;
                if(!vis[v]) {
                    vis[v] = 1;
                    sta[++top] = v;    
                }    
            }     
        }    
    }    
    return dist[t] != inf;
}
void mcmf(int s,int t) {
    maxf = minc = 0;
    while(spfa(s,t)) {
        //printf("hello,world\n");
        int minf = inf;
        for(int i=p[t];i!=-1;i=p[edge[i].u])
            if(edge[i].f < minf) minf = edge[i].f;
        maxf += minf;
        minc += minf * dist[t];
        for(int i=p[t];i!=-1;i=p[edge[i].u]) {
            edge[i].f -= minf;
            edge[i^1].f += minf;    
        }    
    }    
}
int main() {
    int N,M;
    while(~scanf("%d%d",&N,&M)) {
        int s = 0 , t = 2 * N +1;
        n = t + 1;
        init();
        while(M--) {
            int u,v,c;
            scanf("%d%d%d",&u,&v,&c);
            addedge(u,v+N,1,c);    
        }    
        for(int i=1;i<=N;i++) {
            addedge(s,i,1,0);
            addedge(i+N,t,1,0);    
        }
        mcmf(s,t);
        //printf("maxf is %d\n",maxf);
        if(maxf==N) printf("%d\n",minc);
        else printf("-1\n");
    }
    return 0;    
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值