我们要做这道题首先先来学习:

无源汇上下界可行流

什么是无源汇上下界可行流

在一张图中,没有s和t,每条边有流量下界和流量上界,流量在这个区间内,求是否存在一种方案在满足流量平衡的情况下,使所有边满足上下界限制。

先说解法(先接受这种解法再将为什么):

这是一个网络图(图1):

(图1)

以他的每条边的下届为容量再画一个图(图2)

(图2)

在图二上记录每个点为了保持流量平衡,还需要出/入多少流量(这里出/入代表还要出/入多少才可以保证流量平衡(入=出))

(图2)

在根据他每条边上届减去下届的为容量再画一个图(图3):

(图3)

将图4跑一边最大流,如果S的出边和T的入边都是满流(流量=容量),则有解。图4跑完最大流的流量+图二每个边的流量就是一种可行流(不算虚点的)(黑色是容量,红色是实际流量)

(图4)

下面就是可行流的结果

为什么这样是正确的呢?

正确性理解

图2是我们可行流最小的限度,那么可行流一定是在图2(最小流)加上一些值使他流量平衡,这个值肯定是在图3(上下界差)中得出的。

那么这个值到底怎么算出来的呢

首先我们记录了每个点还需要出/入多少流量才能保持流量平衡,如图:


先看点③,还需要流入4的流量才能保持平衡,那么这4的流量一定从别的地方流入,我们让③和T连接一条容量为4的边,并且必须满流(流量也等于4),相当于③必须向T流4的流量,这4的流量必须从别的地方流向③,那么去除虚点后就可以保证有4的流量会流入③,这样图4(差值图/还可以加的图)就抵消了③在图2(最小届)还需要入4的条件。

接着看②,还需要流出2的流量才能保持平衡,那么这2的流量一定从别的地方流出,我们让S和②连接一条容量为2的边,并且必须满流(流量也等于2),相当于S必须向②流2的流量,这2的流量必须向别的地方流出②,那么去除虚点后就可以保证有2的流量会流出②,这样图4(差值图/还可以加的图)就抵消了②在图2(最小届)还需要出2的条件。

点⑤也是同理的哦喵喵~

上面的过程也同时解决了为什么要满流和为什么要跑最大流(最大流可以尽可能保证S,T流出流入的量最多,也就是保证了尽可能满流)和为什么要图4跑完最大流的流量+图二每个边的流量

有源汇上下界可行流

我们在上面考虑无源汇上下界可行流的时候是保证每个点的入流和出流一样就是可行解,现在加入两个点:S和T,只需要再保证S的出=S的入,T的入=T的出就可以直接把有源汇转换成无源汇,现在S有出边,T有入边,并且S的出流=T的入流(这显而易见),我们只用给T一个出边,S一个入边就可行了。

各位巨稍微想一下就可以秒切这个问题:直接从T向S修一条界限为0-inf(这样可以取任何值)的边就可以了

有源汇上下界最大流

搞出可行流,怎么搞最大流呢?

这是作者手胡的一个图:

首先我们再来看看我们的可行流

假设我们跑出来的这是可行流(作者也不知道根据上述方法跑出来可行流是什么,先假设是这个)

ans1=我们建的那个t-s的虚边的val(也就是s-t的流量)

我们用最大界限减去这个可行流,在这个剪出来的图上跑一次最大流ans2:

最大流方案就是把这个红色的val和可行流的val加起来

如果只求一个最后最大流把ans1、ans2加起来就是最后的答案啦!

学会了模板,那么我们接下来来分析题目~~

题目分析

“第x个少女拍摄至少Gx​张照片“则S到少女x建\([G_{x},inf]\)

"在第k天的时候文文有Ck​个取材对象,且对于每个取材对象拍的照片必须在闭区间\([L_{k_i},R_{k_i}]\)"则少女i到天k建\([L_{k_i},R_{k_i}]\)

每天的拍照数量不能超过Di​张",则天i到T建\([0,D_{i}]\)

代码细节

看上去有114514张图,其实你只需要建一张图。

那如何算每个点在图2(最下界图)需要再出/入多少呢?(add函数的2,3行就是解决方法)

这是前向星记录的东西(该记的都记):

这是input函数,注意每次add之前的chu[]++,ru[]++,可以直接算

下面是点的下标

`

s点为1
少女点为2--m+1
天点为m+2--m+n+1
t点为m+n+2
虚拟s为0,虚拟t为m+n+3

`

void input(){
s=1;t=m+n+2;
x_s=0;x_t=m+n+3;
for(int i=1;i<=m;i++){
scanf("%d",&g);
ru[i+1]+=g;
chu[s]+=g;
add(s,i+1,g,inf);
add(i+1,s,0,0);
//add(s,i+1,g,inf);
}
for(int i=1;i<=n;i++){
scanf("%d%d",&c,&d);
ru[t]+=0;
chu[i+m+1]+=0;
add(i+m+1,t,0,d);
add(t,i+m+1,0,0);
for(int j=1;j<=c;j++){
scanf("%d%d%d",&num,&l,&r);
ru[i+m+1]+=l;
chu[num+2]+=l;
add(num+2,i+m+1,l,r);
add(i+m+1,num+2,0,0);
}
}
}

下面是建虚边

void op1(){//建虚边
for(int i=1;i<=m+n+2;i++){//出入的差距向ST建
if(chu[i]>ru[i]){
add(i,x_t,0,chu[i]-ru[i]);
add(x_t,i,0,0);
}else if(chu[i]<ru[i]){
add(x_s,i,0,ru[i]-chu[i]);
add(i,x_s,0,0);
}
}
add(t,s,0,inf);//TS直接修[0,inf]
add(s,t,0,0);
}

上文”最大界限减去这个可行流,在这个剪出来的图上跑一次最大流“这个图怎么得到呢?

其实直接把这些虚边删去,然后从原本s-t跑一次最大流就可以得到ans2

为什么呢?其实最大流-这个可行流=r-(l+在有虚ST通过delta跑最大流)=delta-在有虚ST通过delta跑最大流=在第一次跑完最大流(用dinic或者EK或者其他什么高级算法)delta已经减过了在有虚ST通过delta跑最大流,所以可以直接删去虚点跑最大流。

删去虚点虚边代码:

`

void delet(){
for(int i=head[x_s];i;i=edge[i].next){
edge[i].l=0;
edge[i].r=0;
edge[i].delta=0;
edge[i^1].l=0;
edge[i^1].r=0;
edge[i^1].delta=0;
}
for(int i=head[x_t];i;i=edge[i].next){
edge[i].l=0;
edge[i].r=0;
edge[i].delta=0;
edge[i^1].l=0;
edge[i^1].r=0;
edge[i^1].delta=0;
}
edge[tot].l=0;//tot代表最后一次建的边也就是t-s那个虚边
edge[tot].r=0;
edge[tot].delta=0;
edge[tot^1].l=0;
edge[tot^1].r=0;
edge[tot^1].delta=0;
x_s=s;
x_t=t;
} `

这是判断是否满流:

`

bool pd(){
for(int i=head[x_s];i;i=edge[i].next){
if(edge[i].delta!=0){
return 0;
}
}
return 1;
}`

总代码

`

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10,inf=1e9+7;
int n,m,s,t,g,c,d;//实际st
int x_s,x_t,tot=1,head[N],l,r,num;//虚拟st
int ru[N],chu[N],de[N],now[N],vis[N],bj;
int ans1=0,ans2;
queue<int>q;
//s点为1
//少女点为2--m+1
//天点为m+2--m+n+1
//t点为m+n+2
//虚拟s为0,t为m+n+3
struct node{
int from,next,to,l,r,delta;
}edge[2*N];
void add(int x,int y,int l,int r){
tot++;
edge[tot].from=x;
edge[tot].to=y;
edge[tot].l=l;
edge[tot].r=r;
edge[tot].delta=r-l;
edge[tot].next=head[x];
head[x]=tot;
}
void input(){
s=1;t=m+n+2;
x_s=0;x_t=m+n+3;
for(int i=1;i<=m;i++){
scanf("%d",&g);
ru[i+1]+=g;
chu[s]+=g;
add(s,i+1,g,inf);
add(i+1,s,0,0);
//add(s,i+1,g,inf);
}
for(int i=1;i<=n;i++){
scanf("%d%d",&c,&d);
ru[t]+=0;
chu[i+m+1]+=0;
add(i+m+1,t,0,d);
add(t,i+m+1,0,0);
for(int j=1;j<=c;j++){
scanf("%d%d%d",&num,&l,&r);
ru[i+m+1]+=l;
chu[num+2]+=l;
add(num+2,i+m+1,l,r);
add(i+m+1,num+2,0,0);
}
}
}
bool bfs(){//分层
memset(de,127,sizeof(de));
q.push(x_s);
de[x_s]=1;
vis[x_s]=1;
while(!q.empty()){
int x=q.front();
q.pop();
vis[x]=0;
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(de[y]>de[x]+1&&edge[i].delta){
de[y]=de[x]+1;
if(vis[y]==0){
q.push(y);
}
}
}
}
if(de[x_t]<inf) return 1;
return 0;
}
int dfs(int x,int ans){
int rest=ans;
if(x==x_t) return ans;
vis[x]=1;
for(int i=now[x];i&&rest;i=edge[i].next){
int y=edge[i].to;
now[x]=i;
if(de[y]==de[x]+1&&!vis[y]&&edge[i].delta!=0){
int k=dfs(y,min(rest,edge[i].delta));
if(k){
edge[i].delta-=k;
edge[i^1].delta+=k;
rest-=k;
}
}
}
vis[x]=0;
return ans-rest;
}
void op1(){//在区间最小的时候跑一下出和入
for(int i=1;i<=m+n+2;i++){
if(chu[i]>ru[i]){
add(i,x_t,0,chu[i]-ru[i]);
add(x_t,i,0,0);
}else if(chu[i]<ru[i]){
add(x_s,i,0,ru[i]-chu[i]);
add(i,x_s,0,0);
}
}
add(t,s,0,inf);
add(s,t,0,0);
}
void op2(){//用新建的和delta跑最大流
while(bfs()){
// for(int i=0;i<=n+m+3;i++) printf("%d",de[i]);
// cout<<endl;
for(int i=0;i<=n+m+3;i++) now[i]=head[i];
ans1+=dfs(x_s,inf);
}
}
void delet(){
for(int i=head[x_s];i;i=edge[i].next){
edge[i].l=0;
edge[i].r=0;
edge[i].delta=0;
edge[i^1].l=0;
edge[i^1].r=0;
edge[i^1].delta=0;
}
for(int i=head[x_t];i;i=edge[i].next){
edge[i].l=0;
edge[i].r=0;
edge[i].delta=0;
edge[i^1].l=0;
edge[i^1].r=0;
edge[i^1].delta=0;
}
edge[tot].l=0;
edge[tot].r=0;
edge[tot].delta=0;
edge[tot^1].l=0;
edge[tot^1].r=0;
edge[tot^1].delta=0;
x_s=s;
x_t=t;
}
bool pd(){
for(int i=head[x_s];i;i=edge[i].next){
if(edge[i].delta!=0){
return 0;
}
}
return 1;
}
int main(){
while(cin>>n>>m){
bj=0;
memset(head,0,sizeof(head));
memset(edge,0,sizeof(edge));
memset(chu,0,sizeof(chu));
memset(ru,0,sizeof(ru));
input();
op1();
op2();
if(!pd()){
printf("-1\n\n");
continue;
}
ans2=edge[tot].delta;
ans1=0;
delet();
op2();
printf("%d\n\n",ans1+ans2);
} return 0;
}`

完结撒花~~~~

P5192 Zoj3229 Shoot the Bullet|东方文花帖|【模板】有源汇上下界最大流的更多相关文章

  1. Shoot the Bullet(ZOJ3229)(有源汇上下界最大流)

    描述 ensokyo is a world which exists quietly beside ours, separated by a mystical border. It is a utop ...

  2. zoj 3229 Shoot the Bullet(有源汇上下界最大流)

    Shoot the Bullethttp://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3442 Time Limit: 2 Second ...

  3. 【有源汇上下界最大流】ZOJ 3229 Shoot the Bullet

    题目链接: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3229 题目大意: n天给m个女孩拍照(1<=n<= ...

  4. ZOJ 3229 Shoot the Bullet(有源汇上下界最大流)

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3442 题目大意: 一个屌丝给m个女神拍照,计划拍照n天,每一天屌丝给 ...

  5. zoj3229 有源汇上下界最大流

    题意:有一个人每天给妹子拍照,每个妹子有最少拍照数,每天有最大拍照数,每天只能给某些特定的妹子拍照,求最大拍照数 题解:很容易看出来的有源汇上下界最大流,对于有源汇 的上下界最大流,我们按照无源汇的操 ...

  6. zoj 3229 Shoot the Bullet(无源汇上下界最大流)

    题目:Shoot the Bullet 收藏:http://www.tuicool.com/articles/QRr2Qb 把每一天看成一个点,每个女孩也看成一个点,增加源和汇s.t,源向每一天连上[ ...

  7. ZOJ - 3229 Shoot the Bullet (有源汇点上下界最大流)

    题意:要在n天里给m个女生拍照,每个女生有拍照数量的下限Gi,每天有拍照数量的上限Di,每天当中每个人有拍照的上限Lij和Rij.求在满足限制的基础上,所有人最大能拍多少张照片. 分析:抛开限制,显然 ...

  8. Shoot the Bullet(有源汇带上下界最大流)

    有源汇带上下界最大流 在原图基础上连一条汇点到源点流量为inf的边,将有源汇网络流转化为无源汇网络流用相同方法判断是否满流,如果满流再跑一边源点到汇点的最大流就是答案 例题:Shoot the Bul ...

  9. ZOJ 3229 Shoot the Bullet [上下界最大流]

    ZOJ 3229 Shoot the Bullet 题意:此生无悔入东方 上下界最大流 spj挂掉了我也不知道对不对,把代码放这里吧以后正常了可能会评测一下 #include <iostream ...

  10. 【模板】有源汇有上下界最大流(网络流)/ZOJ3229

    先导知识 无源汇有上下界可行流 题目链接 https://vjudge.net/problem/ZOJ-3229 https://www.luogu.com.cn/problem/P5192 (有改动 ...

随机推荐

  1. pod(九):污点taint 与容忍度tolerations

    目录 一.系统环境 二.前言 三.污点taint 3.1 污点taint概览 3.2 给节点添加污点taint 四.容忍度tolerations 4.1 容忍度tolerations概览 4.2 设置 ...

  2. 如何把Java代码玩出花?JVM Sandbox入门教程与原理浅谈

    在日常业务代码开发中,我们经常接触到AOP,比如熟知的Spring AOP.我们用它来做业务切面,比如登录校验,日志记录,性能监控,全局过滤器等.但Spring AOP有一个局限性,并不是所有的类都托 ...

  3. JUnit 5 单元测试教程

    点赞再看,动力无限. 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章. 在软件开发过程中,我们通常都需要测试 ...

  4. C温故补缺(三):存储类声明符(auto,register,extern,static)

    auto,register,extern,static 四个存储类声明符,用于定义变量/函数的作用域和声明周期 ① auto:自动变量,即普通变量,在平时定义变量时会自动赋予其auto类型 被auto ...

  5. 关于python3调用matplotlib中文乱码问题

    问题描述 我用来绘制柱形图,横坐标上面的数据, 但是网上大部分说的都是更改横纵坐标标签的乱码问题,而不是横坐标数据乱码问题 解决办法 更改横纵坐标上标签的中文不乱码 import matplotlib ...

  6. Java实现递归查询树结构

    我们在实际开发中,肯定会用到树结构,如部门树.菜单树等等.Java后台利用递归思路进行构建树形结构数据,返回给前端,能以下拉菜单等形式进行展示.今天,咱们就来说说怎么样将List集合转换成TreeLi ...

  7. 命令查询职责分离 - CQRS

    概念 CQRS是一种与领域驱动设计和事件溯源相关的架构模式, 它的全称是Command Query Responsibility Segregation, 又叫命令查询职责分离, Greg Young ...

  8. APACHE快速安装流程梳理

    操作参考教程:https://www.cnblogs.com/haw2106/p/9839655.html 快速安装开始: [环境配置1] yum -y install gcc gcc-c++ wge ...

  9. Tekton 设计简介 及 实践

    本文是我对Tekton的实现原理和背后技术逻辑的理解,以及在实践过程中的一些总结. 简介 Tekton 是一个基于 Kubernetes 的云原生 CI/CD 开源(https://cd.founda ...

  10. 浅谈 Java 和 Python 的反射

    反射这个词我一直没搞懂,也不知道为什么需要反射,也不知道反射到底做了什么.所见所闻逐渐丰富之后,开始有点儿懂了. 先不管反射这个词是什么意思.Java 里面有反射,Python 里面也有反射,但是不太 ...