poj2987(网络最大流求最大闭合子图权值)

本文介绍了一种利用网络流算法解决特定图论问题的方法:即在单向图中寻找一个闭合子图,使得该子图的节点权值之和最大,同时选择的节点数尽可能少。通过构建特殊的网络流模型并使用最大流算法,可以有效地求解这个问题。
/*
translation:
    给出一张单向图,每个结点有固定的权值。每次选择一个点的同时,与该点单向连接的点也必须选择(比如u->v,选择u的同时
    也必须选择v,但是对于k->u,k就不是必选的。)现在求一种选择方案使得其选择的权值最大的同时,选择的点数目最小。
solution:
    网络流求最大闭合子图的权值
    具体的做法参考“最小割模型在信息学竞赛中的应用”这一论文的方法即可求出。
note:
date:
    2017.1.29
*/
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>

typedef long long ll;
using namespace std;
const int maxn = 5000 + 5;
const ll INF = 0x3f3f3f3f3f3f3f3f;

struct Edge
{
    int to, rev;
    ll cap;
    Edge(int to_, ll cap_, int rev_):to(to_),cap(cap_),rev(rev_){}
};
vector<Edge> G[maxn];
int level[maxn];
int iter[maxn], n, m; //用于当前弧优化
ll node_val[maxn];
bool vis[maxn];

void add_edge(int from, int to, ll cap)
{
    //printf("build edge from %d to %d: %lld\n", from, to, cap);
    G[from].push_back(Edge(to, cap, G[to].size()));
    G[to].push_back(Edge(from, 0, G[from].size()-1));
}

void bfs(int s)
{
    memset(level, -1, sizeof(level));
    queue<int> que;
    level[s] = 0;
    que.push(s);
    while(!que.empty()) {
        int v = que.front(); que.pop();
        for(int i = 0; i < G[v].size(); i++) {
            Edge& e = G[v][i];
            if(e.cap > 0 && level[e.to] < 0) {
                level[e.to] = level[v] + 1;
                que.push(e.to);
            }
        }
    }
}



ll dfs(int v, int t, ll f)
{
    if(v == t)  return f;
    for(int& i = iter[v]; i < G[v].size(); i++) {
        Edge& e = G[v][i];
        if(e.cap > 0 && level[v] < level[e.to]) {
            int d = dfs(e.to, t, min(f, e.cap));
            if(d > 0) {
                e.cap -= d;
                G[e.to][e.rev].cap += d;
                return d;
            }
        }
    }
    return 0;
}

ll max_flow(int s, int t)
{
    ll flow = 0;
    for(;;) {
        bfs(s);
        if(level[t] < 0)    return flow;
        memset(iter, 0, sizeof(iter));
        ll f;
        while((f = dfs(s, t, INF)) > 0) flow += f;
    }
    //return flow;
}

int ans = 0;
void dfs1(int s)
{
    vis[s] = true;
    //printf("visit %d\n", s);
    ans++;
    for(int i = 0; i < G[s].size(); i++) {
        Edge e = G[s][i];
        if(e.cap > 0 && !vis[e.to])  dfs1(e.to);
    }
}

int main()
{
    //freopen("in.txt", "r", stdin);
    while(~scanf("%d%d", &n, &m)) {
        for(int i = 0; i <= n; i++) G[i].clear();

        int s = 0, t = n + 1;
        ll sum = 0;
        for(int i = 1; i <= n; i++) {
            cin >> node_val[i];
            if(node_val[i] > 0) {
                sum += node_val[i];
                add_edge(s, i, node_val[i]);
            } else if(node_val[i] < 0) {
                add_edge(i, t, -node_val[i]);
            }
        }

        int u, v;   //u->v
        for(int i = 0; i < m; i++) {
            scanf("%d%d", &u, &v);
            add_edge(u, v, INF);
        }

        ans = 0;
        ll ans_val = sum - max_flow(s, t);
        memset(vis, 0, sizeof(vis));
        dfs1(s);
        printf("%d %lld\n", ans-1, ans_val);
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值