托米去购物(最大流)

题目描述:

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

题目描述

此时的托米老师已经出任CEO,迎娶白富美,走向了人生巅峰!于是这个暑假,托米老师打算在北京一个偏僻的小农村里度过他的假期。
由于这里什么都没有,于是他去超市选了很多生活用品,更多的是吃的,然后推着堆满零食的购物车到柜台等待结账。
当然,我们都知道他的钱包里有很多钱。但是,作为一名为生活精打细算的男孩子,他更愿意使用其他支付方式如:饭券,礼券,不同类型的优惠券等。但是饭券只能用于购买食物,而礼券通常只限于某种类型的礼物。
现在给你托米购物车中物品的数量N和每件物品的价格。也会给出他钱包中的代金券数量M以及允许使用的信息 。
在为他的购物付款时,托米可能使用代金券的金额超过他所购物品的成本。也可以在多张代金券之间拆分商品的成本,并使用代金券支付多件商品。
请你计算托米需要为购物支付的额外现金的最小金额。

输入描述:

输入的第一行包含一个整数T,用于指定测试用例的数量。
每个测试用例前面都有一个空白行。
每个测试用例从包含两个正整数N(物品数量)和M(券数量)的行开始。
接下来一行包含N个数字,第i个数字表示托米购物车里第i件物品的价格。
接下来一行包含M个数字,第i个数字表示第i张券的金额。
接下来有M行,当中的第 i 行描述第 i 张卷可以买哪些商品。每行的第一个数字是 K,代表第 i 张卷可以为 K 件商品付款,接下来还有 K 个数,是这 K 件商品的编号

输出描述:

对于每个测试用例输出数字,表示托米需要支付多少现金。
思路:做的第一道网络流问题,感受就是,算法不难,主要还看建图,这道题我们把优惠券也看成物品,在优惠券之后连上可以用来消费的物品,
流量就是物品费用,然后在用一个源点s连上所有优惠券,流量是优惠券的价值,再把物品都连到一个汇点,流量也是物品费用
大概样子就是这样:

S,E分别是源点和汇点,D,E为优惠券,ABC是物品,然后求S到E的最大流(dinc或者ISAP都可以)就是用优惠券能节约的钱,然后用物品总价值减去它就是需要花费的钱
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3000;
const int maxm = 100005;
typedef long long ll;
const ll inf = 1e15;
struct edge {
    int t, nxt;
    ll wight;
}e[maxm];
int hd[maxn], tot;
void add(int f, int t, ll w) {
    e[++tot] = { t,hd[f],w };
    hd[f] = tot;
    e[++tot] = { f, hd[t],0 };
    hd[t] = tot;
}
int cur[maxn], dep[maxn];
bool bfs(int s, int d) {
    queue<int>q;
    memset(dep, -1, sizeof(dep));
    q.push(s);
    dep[s] = 0;
    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].wight;
            if (dep[v] == -1 && w > 0) {
                dep[v] = dep[u] + 1;
                q.push(v);
            }
        }
    }
    return dep[d] != -1;
}
ll dfs(int u, ll flow, int d) {
    if (u == d)return flow;
    ll rest = flow;
    for (int i = cur[u]; i; i = e[i].nxt) {
        cur[u] = i;//优化2,当前弧优化
        //如果走到当前弧时上层给它的流量用光了,
        //之前的路线已经走过了,而且流量是拉满了的(即min(w,rest)取的是rest),下次就不用又走一遍了,与优化1对应起来
        int  v = e[i].t; ll w = e[i].wight;
        if (dep[v] == dep[u] + 1 && w > 0) {
            ll tmp = dfs(v, min(w, rest), d);
            rest -= tmp;
            e[i].wight -= tmp;
            e[i ^ 1].wight += tmp;
            if (rest == 0)break;//优化1,上一层流过来的流量已经分完了,没得分了
        }
    }
    if (rest == flow)dep[u] = -1; //优化3,走这个点的所有边一条增广路都没发现,废点,炸掉
    return flow - rest;//该点的流出的流量就是上一层流过来的减去剩下的流量
}
ll dinic(int s, int d) {
    ll ans = 0;
    while (bfs(s, d)) {
        for (int i = 1; i <= d; i++) { cur[i] = hd[i]; }
        ans += dfs(s, inf, d);
    }
    return ans;
}
int n, m;
ll val[maxn], sum;
int main() {
    //freopen("test.txt", "r", stdin);
    int T; scanf("%d", &T);
    while (T--) {
        sum = 0; tot = 1;
        memset(hd, 0, sizeof(hd));
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n + m; i++) {
            scanf("%lld", &val[i]);
            if (i <= n)sum += val[i];
        }
        int s = n + m + 1, d = n + m + 2;
        for (int i = n + 1; i <= n + m; i++) {
            add(s, i, val[i]);
            int k; scanf("%d", &k);
            while (k--) {
                int t; scanf("%d", &t);
                add(i, t, val[t]);
            }
        }
        for (int i = 1; i <= n; i++) {
            add(i, d, val[i]);
        }
        printf("%lld\n", sum - dinic(s, d));
    }
    return 0;
}

 



posted @ 2021-03-19 15:24  cono奇犽哒  阅读(51)  评论(0)    收藏  举报