新疆大学五月月赛 D 题 【网络流 之 最大权闭合子图】

传送门
题意: 有n门课程, 每门都有一个智慧值和一个智力消耗值. 并且每门课程有0或多个先修课程, 即必须修了先修课程才能修该门课程. 问所有学过的课程的智慧值的和与智力消耗值的和的差值。请问,这个值最大是多少?

思路:有很明显的依赖关系, 如果u 是 v 的先修课程, 那么就连一条有向边v -> u, 然后每个点有自己的权值, 那就是自身的智慧值与智力消耗值的差值, 所以问题就很明显的是求最大权闭合子图. 直接建图, 套板子就即可…

结论: 最大权闭合子图的权值等于所有正权点之和减去最小割

AC Code

const int maxn = 5e2+5;
int cnt ;
int n, m, s, t;
bool vis[maxn];
int d[maxn], cur[maxn];
struct Edge {
    int u, v;
    int cap, flow;
} e[maxn*maxn];    //因为是双向边  所以记得开二倍
vector<int> G[maxn];

void init() {
    for (int i = 0; i <= n + 1; i++)
        G[i].clear();
    cnt = 0;
}

void add(int u, int v, int cap, int f) {
    e[cnt].u = u;
    e[cnt].cap = cap;
    e[cnt].flow = f;
    e[cnt].v = v;
}

void AddEdge(int u, int v, int cap) {
    add(u, v, cap, 0);
    G[u].push_back(cnt++);
    add(v, u, 0, 0);
    G[v].push_back(cnt++);
}

bool BFS() {
    Fill(vis, 0);
    queue<int> q; q.push(s);
    vis[s] = 1; d[s] = 0;
    while (!q.empty()) {
        int v = q.front(); q.pop();
        for (int i = 0; i < G[v].size(); i++) {
            Edge &te = e[G[v][i]];
            if (!vis[te.v] && te.cap > te.flow) { //只考虑残量网络的弧
                vis[te.v] = 1;
                d[te.v] = d[v] + 1;
                q.push(te.v);
            }
        }
    }
    return vis[t];
}

int dfs(int x, int a) {
    if (x == t || a == 0) return a;
    int flow = 0, f;
    for (int &i = cur[x]; i < G[x].size(); i++) { //从上次考虑的弧
        Edge &te = e[G[x][i]];
        if (d[x] + 1 == d[te.v] && (f = dfs(te.v, min(a, te.cap - te.flow))) > 0) {
            te.flow += f;
            e[G[x][i]^1].flow -= f;
            flow += f;
            a -= f;
            if (a == 0) break;
        }
    }
    return flow;
}

int Dinic() {
    int flow = 0;
    while (BFS()) {
        Fill(cur, 0);
        flow += dfs(s, inf);
    }
    return flow;
}

void solve() {
    scanf("%d", &n); init();
    s = 0, t = n + 1; int ans = 0;
    for (int i = 1 ; i <= n ; i ++) {
        int a, b;
        scanf("%d%d", &a, &b);
        a -= b;
        if (a > 0) AddEdge(s, i, a), ans += a;
        if (a < 0) AddEdge(i, t, -a);
    }
    int u, v;
    while(~scanf("%d%d", &u, &v)) {
        AddEdge(v, u, inf);
    }
    printf("%d\n", ans - Dinic());
}

猜你喜欢

转载自blog.csdn.net/anxdada/article/details/80197757