Destroying The Graph(最小割+拆点)

来源:互联网 发布:java调用scala 编辑:程序博客网 时间:2024/06/10 10:34
http://poj.org/problem?id=2125

Destroying The Graph
Time Limit: 2000MS Memory Limit: 65536KTotal Submissions: 7976 Accepted: 2571 Special Judge

Description

Alice and Bob play the following game. First, Alice draws some directed graph with N vertices and M arcs. After that Bob tries to destroy it. In a move he may take any vertex of the graph and remove either all arcs incoming into this vertex, or all arcs outgoing from this vertex.
Alice assigns two costs to each vertex: Wi+ and Wi-. If Bob removes all arcs incoming into the i-th vertex he pays Wi+ dollars to Alice, and if he removes outgoing arcs he pays Wi- dollars.
Find out what minimal sum Bob needs to remove all arcs from the graph.

Input

Input file describes the graph Alice has drawn. The first line of the input file contains N and M (1 <= N <= 100, 1 <= M <= 5000). The second line contains N integer numbers specifying Wi+. The third line defines Wi- in a similar way. All costs are positive and do not exceed 106 . Each of the following M lines contains two integers describing the corresponding arc of the graph. Graph may contain loops and parallel arcs.

Output

On the first line of the output file print W --- the minimal sum Bob must have to remove all arcs from the graph. On the second line print K --- the number of moves Bob needs to do it. After that print K lines that describe Bob's moves. Each line must first contain the number of the vertex and then '+' or '-' character, separated by one space. Character '+' means that Bob removes all arcs incoming into the specified vertex and '-' that Bob removes all arcs outgoing from the specified vertex.

Sample Input

3 61 2 34 2 11 21 13 21 23 12 3

Sample Output

531 +2 -2 +

题意:给你一幅有向图, 对于点i删除所有进入该点的边就要支付费用W[i]+(情况1), 删除所有从该点出发的边就要支付费用W[i]-,问删除图中的所有边至少需要多少费用(情况2)。

分析:首先我们根据题意,选点就能删除一些边, 那么这可以看成是“用点去覆盖边”, 这里无非是把边分成了2类,

我们可以把原来的点进行拆点,那么就完完全全等价于“用点去覆盖边",如果支付费用都为1,那么这就是”最小点覆盖集“问题,但这题费用不确定,那么这就是“最小点权覆盖集”问题, 借助二分匹配的思想,我们可以引入“最小割”来解决“最小点权覆盖”问题。

建图:拆点,左点阵为情况2的点, 右点阵为情况1的点,右点阵跟汇点T连流量为W+,左点阵跟源点S连费用为W-,

对于输入的边<u, v> 连边 (u, v+n)费用为无穷大inf。跑一边最大流,求出最小费用。

输出解:最要我们找到一个满足条件的割边集(注意不是所有割边, 因为有一条流已经经过了一条割边,那么下面一条割边就不用选了,这样费用才是最小的),那么就能输出解了。怎么找出割边呢?我们可以在残余网络里走流,如果有一条边是割边,那么之后就流不过去了,不是割边还能继续流,具体实现我们可以从源点S用dfs搜出能走到的点标记vis[] =1,

那么对于边<u,v> 只要 vis[u] = 1 && vis[v] = 0 那就是割边了。

总结:二分匹配的题都可以用最大流来解,在二分图中 有 “最小点覆盖集”和“最打独立集”,如果有了点权,那么就要用最大流(最小割)来解决 “最小点权覆盖集”(最小割)和“最大点权独立集”(最大流)问题。


#include <cstdio>#include <string.h>#include <queue>#include <algorithm>using namespace std;int const MAX = 55000;int const inf = 0x3f3f3f3f;struct Edge{    int to, next;    int c;}e[MAX];int head[MAX], tot, s, t;int def[MAX];int n, m;void add(int u, int v, int c){    e[++tot].to = v;    e[tot].c = c;    e[tot].next = head[u];    head[u] = tot;}int bfs(){    queue<int> q;    while(!q.empty())        q.pop();    memset(def, 0, sizeof(def));    def[s] = 1;    q.push(s);    while(!q.empty()){        int u = q.front();        q.pop();        for(int i = head[u]; i != -1; i = e[i].next){            int v = e[i].to;            if(!def[v] && e[i].c > 0){                def[v] = def[u] + 1;                q.push(v);            }        }    }    return def[t];}int dfs(int u, int mi){    if(u == t)    return mi;    int tmp;    for(int i = head[u]; i != -1; i = e[i].next){        int v = e[i].to;        if(e[i].c > 0 && def[v] == (def[u] + 1) && (tmp = dfs(v, min(mi, e[i].c))) > 0 ){            e[i].c -=  tmp;            e[i+1].c += tmp;            return tmp;        }    }    return 0;}int dinic(){    int ans = 0, tmp;    while(bfs()){        while(1){            tmp = dfs(0, inf);            if(tmp == 0)                break;            ans += tmp;        }    }    return ans;}int ok[MAX];void DFS(int u){    ok[u] = 1;    for(int i = head[u]; i != -1; i = e[i].next){        int v = e[i].to;        if(ok[v] == 0 && e[i].c > 0)            DFS(v);    }}void solve(){    memset(ok, 0, sizeof(ok));    int ans = dinic();    printf("%d\n", ans);    int res = 0;    DFS(0);    for(int i = 1; i <= n; i++){        /***********************************************************************************************************        巧妙地利用了本题图的特殊性质        A}中不可达的点就是应该执行1操作的点。 残余网络中,左侧不可达的点表示被操作1的流选中了        B}中可达的点就是应该执行2操作的点。 右侧可达的点表示从左侧开始无论如何也没有被流选中,只能留给操作2。        ************************************************************************************************************/        if(!ok[i])            res++;        if(ok[i + n])            res++;    }    printf("%d\n", res);    for(int i = 1; i <= n; i++){        if(!ok[i])            printf("%d -\n", i);        if(ok[i+n])            printf("%d +\n", i);    }}int main(){    int k;    while(scanf("%d %d", &n, &m) != EOF){        tot = 0;        memset(head, -1, sizeof(head));        s = 0, t = (n * 2) + 1;        for(int i = 1; i <= n; i++){            scanf("%d", &k);            add(i + n, t, k);            add(t, i + n, 0);        }        for(int i = 1; i <= n; i++){            scanf("%d", &k);            add(s, i, k);            add(i, s, 0);        }        for(int i = 1; i <= m; i++){            int u, v;            scanf("%d %d", &u, &v);            add(u, v + n, inf);            add(v + n, u, 0);        }        solve();    }    return 0;}


0 0
原创粉丝点击