【luogu P5192】Zoj3229 Shoot the Bullet|东方文花帖|【模板】有源汇上下界最大流(网络流)

Zoj3229 Shoot the Bullet|东方文花帖|【模板】有源汇上下界最大流

题目链接:luogu P5192

题目大意

有 m 个人,每个人有一个总需求值。
然后有 n 天,每天有一些指定的人可以给予需求,每个人可以给定的量是一个区间中的任意一个数,然后限制你每天给的所有人的总量不能超过一个给出的值。
然后问你最多能给多少,如果不能满足要求则输出 -1。

思路

首先我们考虑怎么网络流建图。
你会发现它这个给的量是一个区间感觉每条边的流量最小就不一定是 0 了,那我们先假设我们会怎么做(有源汇上下界最大流)。
那不难得出建图方法:
每天每个点建点,分别是 Pi,Qi
每个人有总需求,QiT 连边,流量是 Giinf
每天有上限,SPi 连边,流量是 0Di
然后第 i 天,每个人 Ti,j 给分配,Pi 连向 QTi,j,流量是 Li,jRi,j

于是问题就变成了如何求有源汇上下界最大流,这题也就是这个东西的模板了。


在求这个东西之前,让我们先从简单的来。

无源汇上下界可行流

一个图中有一些边有流量上下界,然后问你是否存在一个方案使得流量平衡且每条边的流量满足上下界的限制。
(流量平衡就是每个点流入的流量等于流出的流量)

然后有一个贪心的想法:我们把上限减去下线,然后直接跑最大流。
这个想法显然是不行的:
假如有一个 1020 的边,然后后面跟着一个 3040 的边,它们显然是流不了的。
但是减了之后就变成了两个都是 10,就可以流了。

那我们考虑如何处理这个问题,亦或者说如何处理下界删掉的边。
我们其实可以发现问题是这样每个点就可能不满足流量平衡。
那我们其实会想到有一个东西是无所谓流量平衡的。
没错,就是源点汇点。
所以我们可以把那些少了的流量用源汇点来搞。

具体来讲记录入边少了的流量 rui 和出边的 chui
然后因为如果都有其实可以消掉,所以可以这样:
rui>chui:从源点向这个点连流量为 ruichui 的边。
rui=chui:不用管
rui<chui:从这个点向汇点连流量为 chuirui 的边。

然后就直接跑最大流,至于判断是否可能我们可以直接看跟源点汇点相连的,因为它们是下限里面的,所以它们一定要跑满,所以就检查是否跑满即可。
(就是看最大流是否等于跟源点连的边的流量和,源汇点等价看一个即可)

有源汇上下界可行流

考虑有源汇点怎么办。
也就是说又来了两个无所谓流量平衡的点,那就不行了。
但是它们两个一个流出的量是等于一个流入的量的,所以其实可以这样让他们变成流量平衡的:汇点往源点连一条 inf 的边。
然后就当无源汇上下界可行流来做即可。

那这个时候流量其实就可以反映为这条汇点到源点的边的反边在跑了最大流之后的流量。

有源汇上下界最大流

那你求出来的不一定是最大流,毕竟你只是保证了底线,它图里面那些可能还可以增。

那我们假设前面的流量是 ans1,那我们考虑求出 ans2ans1 还能往上浮动的量。
那至于求 ans2,其实不难看出因为你是原图中的边没有流满,你就直接在原图中跑一遍源点到汇点的最大流,就是 ans2 了。

那至此,这道题也就可以做了。

EX:有源汇上下界最小流

其实没什么差别,就是现在是要求一个 ans2 为还能往下浮动的量。
然后答案就是 ans1ans2
那你不难想出这个 ans2 的求法就是跑原图汇点源点的最大流。

代码

#include<queue> #include<cstdio> #include<cstring> #include<iostream> #define INF 0x3f3f3f3f3f3f3f3f using namespace std; const int N = 365 + 10; const int M = 1000 + 10; struct node { int x, to, nxt, op; }e[(M + N + N * 300 + 10000) << 1]; int n, m, g[M], S, T, le[N + M + 10], tot, KK; int ru[N + M + 10], chu[N + M + 10], s1, s2, t1, t2; int dis[N + M + 10], lee[N + M + 10]; void Add(int x, int y, int z) { e[++KK] = (node){z, y, le[x], KK + 1}; le[x] = KK; e[++KK] = (node){0, x, le[y], KK - 1}; le[y] = KK; } bool bfs() { memcpy(lee, le, sizeof(le)); memset(dis, 0x7f, sizeof(dis)); queue <int> q; q.push(S); dis[S] = 0; while (!q.empty()) { int now = q.front(); q.pop(); for (int i = le[now]; i; i = e[i].nxt) if (e[i].x && dis[e[i].to] == dis[0]) { dis[e[i].to] = dis[now] + 1; if (e[i].to == T) return 1; q.push(e[i].to); } } return 0; } int dfs(int now, int sum) { if (now == T) return sum; int go = 0; for (int &i = lee[now]; i; i = e[i].nxt) if (e[i].x && dis[e[i].to] == dis[now] + 1) { int this_go = dfs(e[i].to, min(sum - go, e[i].x)); if (this_go) { e[i].x -= this_go; e[e[i].op].x += this_go; go += this_go; if (go == sum) return go; } } if (go != sum) dis[now] = -1; return go; } int Dinic() { int re = 0; while (bfs()) re += dfs(S, INF); return re; } int main() { while (scanf("%d %d", &n, &m) != EOF) { memset(le, 0, sizeof(le)); KK = 0; int sum = 0; tot = n + m; s1 = ++tot; t1 = ++tot; s2 = ++tot; t2 = ++tot; memset(ru, 0, sizeof(ru)); memset(chu, 0, sizeof(chu)); for (int i = 1; i <= m; i++) { scanf("%d", &g[i]); Add(n + i, t1, INF - g[i]); chu[n + i] += g[i]; ru[t1] += g[i]; } for (int i = 1; i <= n; i++) { int c, d, t, l, r; scanf("%d %d", &c, &d); Add(s1, i, d - 0); for (int j = 1; j <= c; j++) { scanf("%d %d %d", &t, &l, &r); t++; Add(i, n + t, r - l); chu[i] += l; ru[n + t] += l; } } for (int i = 1; i <= t1; i++) {//这里可以这么理解,你ru和chu分别是入边和出边省略的流量,那都有的可以抵消,但是没有的话你要通过跟源汇点连接来表示,所以是哪边多连向哪边 if (ru[i] < chu[i]) sum += chu[i] - ru[i], Add(i, t2, chu[i] - ru[i]); else if (ru[i] > chu[i]) Add(s2, i, ru[i] - chu[i]); } Add(t1, s1, INF); S = s2; T = t2; if (Dinic() != sum) { printf("-1\n\n"); continue; } int ans = e[KK].x; e[KK].x = e[e[KK].op].x = 0; S = s1; T = t1; ans += Dinic(); printf("%d\n\n", ans); } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_P5192.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(35)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
历史上的今天:
2021-03-15 【ybt高效进阶4-2-4】【POJ 3468】区间修改区间查询 / A Simple Problem with Integers
点击右上角即可分享
微信分享提示