勤奋的杨老师(二)(最大流)

题目描述:

链接:https://ac.nowcoder.com/acm/problem/15828
来源:牛客网

众所周知,杨老师是一位十分勤奋的老师,他非常的热爱学习。

勤奋的他为自己罗列了一个学习清单,共有n个知识点,他可以有选择的进行学习。

每个知识点都会对应0个或1个或多个先修知识点(只有学会了先修知识点才能学习该知识点),同时每个知识点都有一个智慧值和一个智力消耗值。

杨老师希望在进行过激烈的学习之后,他的收获可以·量化为所有学过的题的智慧值的和与智力消耗值的和的差值。请问,这个值最大是多少?

输入描述:

第一行:一个整数n(n<=500)接下来n行,每行两个整数,代表第i个知识点的智慧值和智力消耗值接下来若干行,每行2个整数u, v,代表u是v的先修知识点。

输出描述:

一行,表示杨老师的收获的最大值
思路:看的题解,这道题一眼看过去都感觉要用树形dp了,但是这里面的图有闭合,不能用,
而最大流却能解决这种问题,设cnt为所有直接选取为赚的节点的和,v为该点获取智慧值-消耗智慧值,如果为负,则源点与其连边,流量为-v
如果为正,则该点与汇点连边,流量为v,如果a是b的前置点,这a与b连边,流量为inf,然后跑一遍最大流,
得到的就是所有直接选取为负的节点,它对以它为前驱(直接或者间接)的其余直接选取为正的点的影响,然后用tot减去该影响,就是能获取的
最大价值.
下面是以样例为例子花的图:

如图跑最大流得到的就是3,4号点对后面1,2号点的取值的影响,减去这部分影响,就是最大价值
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 505;
const int maxm = 60005;
const int inf = 0x3f3f3f3f;//之前搞成1e15,有精度损失..
double esp = 1e-6;
struct edge {
    int t, nxt;
    int w;
}e[maxm];
int hd[maxn], tot = 1;
void add(int f, int t, int w) {
    e[++tot] = {t,hd[f],w};
    hd[f] = tot;
    e[++tot] = {f,hd[t],0};
    hd[t] = tot;
}
int n, s, t;
int cur[maxn], dep[maxn];
bool bfs() {
    memset(dep, 0, sizeof(dep));
    dep[s] = 1;
    queue<int>q;
    q.push(s);
    cur[s] = hd[s];
    while (!q.empty()) {
        int u = q.front(); q.pop();
        for (int i = hd[u]; i; i = e[i].nxt) {
            int v = e[i].t, w = e[i].w;
            if (w > 0 && !dep[v]) {
                dep[v] = dep[u] + 1;
                cur[v] = hd[v];
                if (v == t)return true;
                q.push(v);
            }
        }
    }
    return false;
}
int dfs(int u, int flow) {
    if (u == t)return flow;
    int rest = flow;
    for (int i = cur[u]; i; i = e[i].nxt) {
        int v = e[i].t, w = e[i].w;
        if (dep[v] == dep[u] + 1 && w > 0) {
            int tmp = dfs(v, min(rest, w));
            if (!tmp)dep[v] = 0;
            rest -= tmp;
            e[i].w -= tmp;
            e[i ^ 1].w += tmp;
            if (!rest)break;
        }
    }
    return flow - rest;
}
int dinic() {
    int ans = 0;
    while (bfs()) {
        ans += dfs(s, inf);
    }
    return ans;
}
int main() {
    //freopen("test.txt", "r", stdin);
    scanf("%d", &n);
    s = n+1, t = n + 2;
    int a, b,cnt=0;
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &a, &b);
        int v = a - b;
        if (v > 0) {//如果大于零直接与汇点相连
            cnt += v; add(i, t, v);
        }
        else {//小于零,则与源点相连
            add(s, i, -v);
        }
    }
    int u, v;
    while (~scanf("%d%d", &u, &v)) {
        add(u, v,inf);
    }
    cout <<cnt- dinic() << endl;//减去所有负起点能够流出的最大流
    return 0;
}

 



posted @ 2021-03-19 20:47  cono奇犽哒  阅读(71)  评论(0)    收藏  举报