POJ 2125 Destroying The Graph (最小割的最小权覆盖问题)

题意

给定一个有向图 对于每个点可以毁坏相应的所有入边和出边 所有出边和入边都对应着相应的权值即成本 问若要完全毁坏这张图(即将图中的所有边移除) 所花费最小值是多少

思路

先拆点 将所有点分为出点和入点 将源点连向所有入点 边的权值为删去此点所有入边所花费的值 将所有出点连向汇点 边的权值为删去此点所有出边所花费的权值 再根据题意连两个不同点之间的边 求最小割 即删去哪些边使得S T不连通所花费的最小值
求删的方案可以用dfs来求最后所有与S相连的点 并标记 所有与S不相连的点即删去了这个点的所有入边 同理求删的所有出边
(多加一句话 对于一个二分图若求最小权覆盖 割掉哪条边就是选择相对应的哪个点 从而加上S到那个点的边权即点权)

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>

using namespace std;

const int N = 210, M = 20010, INF = 1e8;

int n, m, S, T;
int h[N], e[M], ne[M], f[M], idx;
int cur[N], d[N], q[N];

bool st[N];

void add(int a, int b, int c)
{
    e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
    e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}

bool bfs()
{
    int hh = 0, tt = 0;
    memset(d, -1, sizeof d);
    q[0] = S, d[S] = 0, cur[S] = h[S];
    while (hh <= tt)
    {
        int t = q[hh ++ ];
        for (int i = h[t]; ~i; i = ne[i])
        {
            int ver = e[i];
            if (d[ver] == -1 && f[i])
            {
                d[ver] = d[t] + 1;
                cur[ver] = h[ver];
                if (ver == T) return true;
                q[ ++ tt] = ver;
            }
        }
    }
    return false;
}

int find(int u, int limit)
{
    if (u == T) return limit;
    int flow = 0;
    for (int i = cur[u]; ~i && flow < limit; i = ne[i])
    {
        cur[u] = i;
        int ver = e[i];
        if (d[ver] == d[u] + 1 && f[i])
        {
            int t = find(ver, min(f[i], limit - flow));
            if (!t) d[ver] = -1;
            f[i] -= t, f[i ^ 1] += t, flow += t;
        }
    }
    return flow;
}

int dinic()
{
    int r = 0, flow;
    while (bfs()) while (flow = find(S, INF)) r += flow;
    return r;
}

void dfs(int u)
{
    st[u] = true;
    for (int i = h[u]; ~i; i = ne[i])
    {
        int j = e[i];
        if (f[i] && !st[j]) dfs(j);
    }
}

int main()
{
    memset(h, -1, sizeof h);
    scanf("%d%d", &n, &m);
    
    S = 0, T = n * 2 + 1;
    
    for (int i = 1; i <= n; i ++ )
    {
        int w;
        scanf("%d", &w);
        add(S, i, w);
    }
    
    for (int i = 1; i <= n; i ++ )
    {
        int w;
        scanf("%d", &w);
        add(i + n, T, w);
    }
    
    for (int i = 0; i < m; i ++ )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(b, a + n, INF);
    }
    
    printf("%d\n", dinic());
    
    dfs(S);
    
    int cnt = 0;
    for (int i = 0; i < idx; i += 2)
    {
        int a = e[i ^ 1], b = e[i];
        if (st[a] && !st[b]) cnt ++ ;
    }
    
    printf("%d\n", cnt);
    
    for (int i = 0; i < idx; i += 2)
    {
        int a = e[i ^ 1], b = e[i];
        if (st[a] && !st[b])
        {
            if (a == S) printf("%d +\n", b);
            if (b == T) printf("%d -\n", a - n);
        }
    }
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值