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

无源汇上下界可行流

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

在一张图中,没有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. Redisson源码解读-分布式锁

    前言 Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid).Redisson有一样功能是可重入的分布式锁.本文来讨论一下这个功能的特点以及源 ...

  2. 通过Jenkins,执行远程服务器的自动化脚本

    通过Jenkins,可自动执行写好的应用部署.自动化测试等的脚本工具,实现测试环境的应用自动定时更新.自动执行测试等. 1. 安装SSH插件 进入[系统管理]-[插件管理]-[可用插件],搜索Publ ...

  3. Dockerfile 使用 SSH docker build

    如果在书写 Dockerfile 时,有些命令需要使用到 SSH 连接,比如从私有仓库下载文件等,那么我们应该怎么做呢? Dockerfile 文件配置 为了使得 Dockerfile 文件中的命令可 ...

  4. 使用CRD扩展Kubernetes API

    本文是如何创建 CRD 来扩展 Kubernetes API 的教程.CRD 是用来扩展 Kubernetes 最常用的方式,在 Service Mesh 和 Operator 中也被大量使用.因此读 ...

  5. Go语言核心36讲06

    我已经为你打开了Go语言编程之门,并向你展示了"程序从初建到拆分,再到模块化"的基本演化路径. 一个编程老手让程序完成基本演化,可能也就需要几十分钟甚至十几分钟,因为他们一开始就会 ...

  6. oracle问题记录

    ORA-01034: ORACLE not available [oracle@ilanni ~]$ sqlplus /nolog SQL*Plus: Release 10.2.0.1.0 – Pro ...

  7. KafkaOffsetMonitor:监控消费者和延迟的队列

    个人名片: 因为云计算成为了监控工程师‍ 个人博客:念舒_C.ying CSDN主页️:念舒_C.ying 目录 消费者组列表 消费组的topic列表 图中参数含义解释如下: topic的历史位置 O ...

  8. halcon如何识别硬币?

    halcon如何识别硬币? 前言 最近一直在学习halcon,在此做了一个案例,分享给大家,效果图如下: 1.思路分析 通过观察,发现1元,5角,1角,它们在面值的文字描述不一样,硬币显示的花纹不一样 ...

  9. hashlib加密 logging日志 subprocess

    Day23 hashlib加密 logging日志 hahlib加密模块 logging日志模块 subprocess模块 1.hahlib加密模块 1.什么是加密? 将明文数据处理成密文数据的过程 ...

  10. JavaScript合集(流程控制语句)

    流程控制 条件判断语句 条件分支语句 循环语句 条件判断语句 if语句 语法: if(条件表达式){ 语句 } ------- if(a > 10){ alert('a比10大') } if-e ...