hdu 4067 #最小费用流

来源:互联网 发布:淘宝推广文案怎么写 编辑:程序博客网 时间:2024/06/05 14:37


题意是问是否能通过删除边后存在一个起点出度-入度为1,终点入度-出度为1,其余点出度==入度的图。

同时删边与留边都要话费,要求最后花费最少。


类似混合欧拉图

因为可以把每个点的度数看作要运送的物品。最后出度大的点连向原点,入度大的点连向汇点,//注意连的方向,不然要wa

关于最小值

默认都取操作最小值,即

如果删的费用大即留该条边,此时起点度数-- ,终点度数++,再添加一条从终点到起点,流量为1(/度数运输),费用为b-a的边,(可以在流动时允许删掉这条边,将终点的一个度数运输到起点,花费了b-a)。

如果留的费用打即删该条边,即不更新度数,建立一条从起点到终点,流量为1 , 费用为a-b的边。


当然也可以不这么建图,比如初始就是所有都留的数据,再都建b-a从终点到起点的边,但注意不要建重边,即没条边在途中只对应一条边。


ok , can you hear me ? 

#include<iostream>#include<cstdio>#include<cstring>#include <queue>using namespace std;int  n , m ;int deg[200] ;typedef long long ll ;#define type llconst int maxn = 2200 ;const int maxm = 200200 ;const int INF = 0x3f3f3f3f ;int s, t;struct node {    int u, v, next;    type cap, flow, cost;}edge[maxm];int head[maxn], cnt;int pre[maxn];type dis[maxn];bool vis[maxn];int N;void init () {    memset (head, -1, sizeof head);    cnt = 0;}void add_edge (int u, int v, type cap, type cost) {    edge[cnt].u = u, edge[cnt].v = v, edge[cnt].cap = cap, edge[cnt].flow = 0;    edge[cnt].cost = cost, edge[cnt].next = head[u], head[u] = cnt++;    edge[cnt].u = v, edge[cnt].v = u, edge[cnt].cap = 0, edge[cnt].flow = 0;    edge[cnt].cost = -cost, edge[cnt].next = head[v], head[v] = cnt++;}bool spfa (int s, int t) {    queue <int> q;    for (int i = 0; i < N; i++) {        dis[i] = INF;        vis[i] = 0;        pre[i] = -1;    }    dis[s] = 0;    vis[s] = 1;    q.push (s);    while (!q.empty ()) {        int u = q.front (); q.pop ();        vis[u] = 0;        for (int i = head[u]; i != -1; i = edge[i].next) {            int v = edge[i].v;            if (edge[i].cap > edge[i].flow && dis[v] > dis[u]+edge[i].cost) {                dis[v] = dis[u]+edge[i].cost;                pre[v] = i;                if (!vis[v]) {                    vis[v] = 1;                    q.push (v);                }            }        }    }    if (pre[t] == -1)        return 0;    else        return 1;}int MCMF (int s, int t, type &cost) {    type flow = 0;    cost = 0;    while (spfa (s, t)) {        type Min = INF;        for (int i = pre[t]; i != -1; i = pre[edge[i^1].v]) {            if (Min > edge[i].cap-edge[i].flow) {                Min = edge[i].cap-edge[i].flow;            }        }        for (int i = pre[t]; i != -1; i = pre[edge[i^1].v]) {            edge[i].flow += Min;            edge[i^1].flow -= Min;            cost += edge[i].cost*Min;        }        flow += Min;    }    return flow;}int main(){    int kase , tt = 1 ;    scanf("%d" , &kase) ;    int st , ed , u , v , a , b ;    while( kase -- ){        scanf("%d %d %d %d" , &n , &m , &st , &ed ) ;        init() ;        N = n + 2 ; /// point actually        memset( edge , 0 , sizeof( edge )) ;        memset(deg , 0 , sizeof( deg )) ;        ll ans = 0 ;        for(int i = 0 ; i < m ; i ++ ){            scanf("%d %d %d %d" , &u , &v , &a , &b ) ;            //add_edge(u , v , 1 , 0 ) ;            if( a >= b ){ add_edge(u , v , 1 , a-b) ; ans += b ;  }            else if( a < b ){ add_edge(v , u , 1 , b-a) ; deg[u] -- , deg[v] ++ ; ans += a ; }        }        ll an = 0 ;        deg[st] ++ ; deg[ed] -- ;        int total_flow = 0 ;        for(int i = 1 ; i <= n ; i ++ ){            if( deg[i] >= 0 ){                add_edge(0 , i , deg[i] , 0 ) ;                total_flow += deg[i] ;            }else if( deg[i] < 0 ){                add_edge(i , n+1 ,  -deg[i] , 0 ) ;            }        }        int temp_flow = MCMF(0 , n+1 , an) ;        //for( int i = 1 ; i <= n ; i ++ ) cout << deg[i] << " "  ; cout << endl ;        //cout << temp_flow << " " << total_flow << endl ;        //cout << ans << " " << an << endl ;        if( temp_flow == total_flow )            printf("Case %d: %lld\n" , tt ++ , an + ans) ;        else            printf("Case %d: impossible\n" , tt ++) ;    }    return 0 ;}