POJ 2125 --Destroying The Graph【最小割解决 "最小点权覆盖问题" && 输出解(割边集) && 各种不懂】

Destroying The Graph
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 7597   Accepted: 2434   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 10 6 . 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 6
1 2 3
4 2 1
1 2
1 1
3 2
1 2
3 1
2 3

Sample Output

5
3
1 +
2 -
2 +

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


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

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

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

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


输出解:不理解啊。

void solve(int u){
    mark[u] = 1;
    for(int i = head[u]; i != -1; i = edge[i].next){
        int v = edge[i].v;
        if(edge[i].cap - edge[i].flow > 0 && !mark[v])
            solve(v);
    }
}


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

以上解析来自http://blog.csdn.net/pi9nc/article/details/27112091


看了别人的解析一下午,对于最小割集的那一块还是不懂, 其他大神还都是用sap写的,sap输出解集和dinic的输出解集的代码还不一样, 而且对最小割不够理解,只能一点一点的调试,试了一下午终于试了出。醉了醉了,理论不扎实,只会用模板的诟病。奋斗奋斗,要理解最大流和最小割 。


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>
#define maxn 5500
#define maxm 1000000
#define INF 0x3f3f3f3f
using namespace std;
int m, n;
int head[maxn],cur[maxn], cnt;
int dist[maxn], vis[maxn];
int mark[maxn];
struct node{
    int u, v, cap, flow, next;
};
node edge[maxm];

void init(){
    cnt = 0;
    memset(head, -1, sizeof(head));
    memset(mark, 0, sizeof(mark));
}

void add(int u, int v, int w){
    node E1 = {u, v, w, 0, head[u]};
    edge[cnt] = E1;
    head[u] = cnt++;
    node E2 = {v, u, 0, 0, head[v]};
    edge[cnt] = E2;
    head[v] = cnt++;
}

void getmap(){
    int a, u ,v;
    for(int i = 1; i <= n; ++i){
        scanf("%d", &a);
        add(i + n, n * 2 + 1, a);
    }
    for(int i = 1; i <= n; ++i){
        scanf("%d", &a);
        add(0, i, a);
    }
    while(m--){
        scanf("%d%d", &u, &v);
        add(u, v + n, INF);
    }
}

bool BFS(int st ,int ed){
    queue<int>q;
    memset(vis, 0 ,sizeof(vis));
    memset(dist, -1, sizeof(dist));
    vis[st] = 1;
    dist[st] = 0;
    q.push(st);
    while(!q.empty()){
        int u = q.front();
        q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            node E = edge[i];
            if(!vis[E.v] && E.cap > E.flow){
                vis[E.v] = 1;
                dist[E.v] = dist[u] + 1;
                if(E.v == ed)
                    return true;
                q.push(E.v);
            }
        }
    }
    return false;
}

int DFS(int x, int ed, int a){
    if(x == ed || a == 0)
        return a;
    int flow = 0, f;
    for(int &i = cur[x]; i != -1; i = edge[i].next){
        node &E = edge[i];
        if(dist[E.v] == dist[x] + 1 && (f = DFS(E.v, ed, min(a, E.cap - E.flow))) > 0){
            E.flow += f;
            edge[i ^ 1].flow -= f;
            a -= f;
            flow += f;
            if(a == 0)
                break;
        }
    }
    return flow;
}

int maxflow(int st, int ed){
    int flowsum = 0;
    while(BFS(st,ed)){
        memcpy(cur, head, sizeof(head));
        flowsum += DFS(st, ed, INF);
    }
    return flowsum;
}

void solve(int u){
    mark[u] = 1;
    for(int i = head[u]; i != -1; i = edge[i].next){
        int v = edge[i].v;
        if(edge[i].cap - edge[i].flow > 0 && !mark[v])
            solve(v);
    }
}

int main (){
    while(scanf("%d%d", &n, &m) != EOF){
        init();
        getmap();
        printf("%d\n", maxflow(0, 2 * n + 1));
        solve(0);
        int ans= 0;
        for(int i = 1; i <= n; ++i){
            if(!mark[i]) ans++;
            if(mark[i + n]) ans++;
        }
        printf("%d\n", ans);
        for(int i = 1;i <= n; ++i){
            if(!mark[i]) printf("%d -\n", i);
            if(mark[i + n]) printf("%d +\n", i);
        }
    }
    return 0;
}


你可能感兴趣的:(POJ 2125 --Destroying The Graph【最小割解决 "最小点权覆盖问题" && 输出解(割边集) && 各种不懂】)