[AHOI2014&&JSOI2014][bzoj3876] 支线剧情 [上下界费用流]
题面
思路
转化模型:给一张有向无环图,每次你可以选择一条路径走,花费的时间为路径上边权的总和,问要使所有边都被走至少一遍(可以重复),至少需要花费多久
走至少一遍,等价于覆盖这条边
也就是说,我们要找这个图的一个可重复的路径覆盖
路径覆盖让我们想到什么算法了呢?
网络流啊!
我们考虑建立网络流图模型。
这道题里面有个关键:走过一条边,走几次就要花几次的费用,这想到什么?
费用流嘛!
我们定义走一次路径会给这条路径上的所有边增加1的流量,再给所有边赋一个费用等于边权
这样,我们只要设每条边的流量有一个1的下限,上限为无限大,就能做了
还要把所有的剧情结束点(没有出边的)连到一个超级汇点,源点就是1号点
跑一个最小费用可行流即可
这里附上最小费用可行流的教程
最小费用可行流
考虑一张网络流图,每条边定义为(u,v,w,l,r),代表从u到v的一条有向边,费用为w,容量为[l,r]闭区间,源点s汇点t已知,且保证源点没有入边、汇点没有出边
同时定义常规费用流图的边为(u,v,w,cap)
现在我们需要求这张图的最小费用可行流(就是满足所有边的流量上下限制,同时费用最小)
按照如下方式建立附加边和附加点:
1.建立附加源点SS,和附加汇点TT
2.对于原图中每一个点(包括源汇)u,令d[u]代表u点的所有入边的流量下界减去出边的流量下界
2.1.如果d[u]是负数,那么从u连一条边(u,TT,0,-d[u])到TT
2.2.如果d[u]是正数,那么从SS连一条边(SS,u,0,d[u])到u
3.对于原图中每一条边(u,v,w,l,r),连边(u,v,w,r-l)
4.连边(t,s,0,inf)(注意这里是原图的源汇点!不是附加的源汇点!!)
这样以后,从SS到TT跑新图的最小费用最大流,再加上原图中每条边的下界流量乘以费用(必须跑的部分),就是最小费用可行流的费用了
为什么?
我们考虑一个点,流入边流量下界比流出边流量下界大1,即d[u]==1
此时,我们要有一个“补流”的思想
此时出小于入,那么出边的流量下界就会比入边的小1
因为下界一定是要满的,而我们如果希望消除下界影响,新图中的旧图的边,流量上届一定是(r-l)
那我们势必要找一个方法,令这个比较小的流量流出下界,能与比较大的流量流入下界“平起平坐”
这个时候,假如我们从超级源补1的流量过来,那是不是相当于“帮了”输出边一把,平衡了一下“实力强大”的输入边呢?
这样我们就完成了补流过程
上面那段是感性理解,如果想看证明的话,可以看看神犇的博客
Code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
int inf=1e9+7;
using namespace std;
inline int read(){
int re=0,flag=1;char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') flag=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
int n,ans,first[510],cnt=-1;
int dis[510],vis[510],q[20010],head,tail;
struct edge{
int to,next,w,cap;
}a[100010];
inline void add(int u,int v,int w,int cap){
a[++cnt]=(edge){v,first[u],w,cap};first[u]=cnt;
a[++cnt]=(edge){u,first[v],-w,0};first[v]=cnt;
}
bool spfa(int s,int t){
int i,u,v,w;head=0,tail=1;
memset(dis,-1,sizeof(dis));memset(vis,0,sizeof(vis));
q[0]=t;dis[t]=0;vis[t]=1;
while(head<tail){
u=q[head++];vis[u]=0;
for(i=first[u];~i;i=a[i].next){
if(!a[i^1].cap) continue;
v=a[i].to;w=a[i].w;
if(dis[v]==-1||dis[v]>dis[u]-w){
dis[v]=dis[u]-w;
if(!vis[v]){
vis[v]=1,q[tail++]=v;
}
}
}
}
return ~dis[s];
}
int dfs(int u,int t,int limit){
if(u==t){vis[t]=1;return limit;}
int i,v,f,flow=0;vis[u]=1;
for(i=first[u];~i;i=a[i].next){
v=a[i].to;
if((dis[v]==dis[u]-a[i].w)&&(a[i].cap)&&(!vis[v])){
f=dfs(v,t,min(limit,a[i].cap));
if(f){
flow+=f;limit-=f;
ans+=f*a[i].w;
a[i].cap-=f;a[i^1].cap+=f;
if(!limit) return flow;
}
}
}
return flow;
}
int costflow(int s,int t){//我写的是从某博客上学的改进版zkw费用流
int re=0;
while(spfa(s,t)){
vis[t]=1;
while(vis[t]){
memset(vis,0,sizeof(vis));
re+=dfs(s,t,inf);
}
}
return re;
}
int d[510];
int main(){
memset(first,-1,sizeof(first));
n=read();int i,t1,t2,t3,j;
for(i=1;i<=n;i++){
t1=read();
for(j=1;j<=t1;j++){
t2=read();t3=read();
d[i]--;d[t2]++;ans+=t3;//流量下界其实都是一
add(i,t2,t3,inf);
}
}
for(i=2;i<=n;i++){
add(i,n+1,0,inf);
}
for(i=1;i<=n;i++){//补流过程
if(d[i]>0) add(0,i,0,d[i]);
if(d[i]<0) add(i,n+2,0,-d[i]);
}
add(n+1,1,0,inf);
costflow(0,n+2);
cout<<ans<<endl;
}
[AHOI2014&&JSOI2014][bzoj3876] 支线剧情 [上下界费用流]的更多相关文章
- BZOJ 3876: [Ahoi2014]支线剧情 [上下界费用流]
3876: [Ahoi2014]支线剧情 题意:每次只能从1开始,每条边至少经过一次,有边权,求最小花费 裸上下界费用流...每条边下界为1就行了 注意要加上下界*边权 #include <io ...
- [bzoj3876][AHOI2014]支线剧情——上下界费用流
题目 传送门 题解 建立s和t,然后s向1连下限0上限inf费用0的边,除1外所有节点向t连下限0上限inf费用0的边,对于每条边下限为1上限为inf费用为经过费用,然后我们只有做上下界网络流构出新图 ...
- BZOJ 3876 支线剧情 | 有下界费用流
BZOJ 3876 支线剧情 | 有下界费用流 题意 这题题面搞得我看了半天没看懂--是这样的,原题中的"剧情"指的是边,"剧情点"指的才是点. 题面翻译过来大 ...
- 【BZOJ3876】[Ahoi2014]支线剧情 有上下界费用流
[BZOJ3876][Ahoi2014]支线剧情 Description [故事背景] 宅男JYY非常喜欢玩RPG游戏,比如仙剑,轩辕剑等等.不过JYY喜欢的并不是战斗场景,而是类似电视剧一般的充满恩 ...
- 【有源汇上下界费用流】BZOJ 3876 [Ahoi2014]支线剧情
题目链接: http://www.lydsy.com:808/JudgeOnline/problem.php?id=3876 题目大意: 给定一张拓扑图(有向无环图),每条边有边权,每次只能从第一个点 ...
- 「AHOI2014/JSOI2014」支线剧情
「AHOI2014/JSOI2014」支线剧情 传送门 上下界网络流. 以 \(1\) 号节点为源点 \(s\) ,新建一个汇点 \(t\),如果 \(u\) 能到 \(v\),那么连边 \(u \t ...
- BZOJ.1927.[SDOI2010]星际竞速(无源汇上下界费用流SPFA /最小路径覆盖)
题目链接 上下界费用流: /* 每个点i恰好(最少+最多)经过一次->拆点(最多)+限制流量下界(i,i',[1,1],0)(最少) 然后无源汇可行流 不需要源汇. 注: SS只会连i',求SS ...
- BZOJ2324 ZJOI2011营救皮卡丘(floyd+上下界费用流)
虽然不一定每次都是由编号小的点向编号大的走,但一个人摧毁的顺序一定是从编号小的到编号大的.那么在摧毁据点x的过程中,其只能经过编号小于x的点.并且这样一定合法,因为可以控制其他人先去摧毁所经过的点.那 ...
- 【BZOJ2055】80人环游世界 有上下界费用流
[BZOJ2055]80人环游世界 Description 想必大家都看过成龙大哥的<80天环游世界>,里面的紧张刺激的打斗场面一定给你留下了深刻的印象.现在就有这么 一个 ...
随机推荐
- 2017.12.25 Linux系统的使用
Linux系统的使用 现在标配的系统是 Linux + Nginx + PHP + MySQL ,这样的配置越来越多的大公司在用的了说到配置不同的是一个公司的规约,比如说挂载一般分为2个盘, / 下面 ...
- Java实现随机出题,10道10以内加减法计算
package com.swift; import java.awt.Toolkit; import java.util.Scanner; public class PlusQuiz { public ...
- 【前端_js】前端跨网络异步获取资源——fetch()
Fetch API 提供了一个 JavaScript接口,用于访问和操纵HTTP管道的部分,例如请求和响应.它还提供了一个全局 fetch()方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源 ...
- 【Python学习之十】yield之send方法
yield作用 简单地讲,yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator.下面以斐波拉 ...
- vue.js 一(基础环境搭建)
之前由于看了React的东西,写了两篇React的脚手架搭建,一是给自己记一点可用的东西,免得每次都去找,毕竟搭建环境在项目相对固定的时期不是经常要干的事情,一段时间不用就会忘记了. 前端的js框架还 ...
- 读书笔记-JavaScript面向对象编程(二)
第5章 原型 5.1 原型属性(所有函数拥有一个prototype属性,默认为空对象) 5.1.1 利用原型添加方法和属性 function Gadget(name,color){ this.name ...
- 常用模块之 os,json,shelve,xml模块
os 即操作系统 在 os 中提供了很多关于文件,文件夹,路径处理的函数 这是我们学习的重点 os.path 是os模块下专门用于处理路径相关的 python是一门跨平台语言,由于每个平台路径规则不同 ...
- GTF/GFF
- day14之模块
一.列表生成式与生成表达式 1.列表生成式(数据量小) 要求:在列表内造100个元素 示例: l=[] for i in range(100): l.append('egg%s'%i) print(l ...
- models管理类抽取基类
Models类 models.py # coding:utf-8 from django.db import models from db.Base_model import Base_Model f ...