[AHOI2014][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;
}

原文地址:https://www.cnblogs.com/dedicatus545/p/8729360.html

时间: 2024-11-05 21:57:23

[AHOI2014][bzoj3876] 支线剧情 [上下界费用流]的相关文章

P4043 [AHOI2014/JSOI2014]支线剧情 上下界费用流

题意: 有个人每次可以从1出发(可以无限次)  走有向边  耗费的时间为有向边的长度   问最少耗费的时间遍历所有的边至少一次 有点像滑雪那题  不过那题求得是最少的次数 这题很显然可以转化为上下界费用流  只要设置边的容量为1-inf 即可 注意: 上下界费用流的答案为: 答案即为(求出的费用+原图中边的下界*边的费用) 答案即为(求出的费用+原图中边的下界*边的费用) 答案即为(求出的费用+原图中边的下界*边的费用) 答案即为(求出的费用+原图中边的下界*边的费用) #include<bit

BZOJ 3876 支线剧情 | 有下界费用流

BZOJ 3876 支线剧情 | 有下界费用流 题意 这题题面搞得我看了半天没看懂--是这样的,原题中的"剧情"指的是边,"剧情点"指的才是点. 题面翻译过来大概是这样: 有一个DAG,每次从1号点出发,走过一条路径,再瞬移回1号点.问:想要遍历所有的边,至少要走多少路程(瞬移回1号点不算路程). 题解 我们用有上下界费用流的模型,建个图: 原图中的每条边,流量范围是\([1, +\infty]\),表示至少走一次,可以走无限次,这条边的费用就是边权. 原图中的每个

【bzoj1927】[Sdoi2010]星际竞速 有上下界费用流

原文地址:http://www.cnblogs.com/GXZlegend/p/6832464.html 题目描述 10年一度的银河系赛车大赛又要开始了.作为全银河最盛大的活动之一,夺得这个项目的冠军无疑是很多人的梦想,来自杰森座α星的悠悠也是其中之一.赛车大赛的赛场由N颗行星和M条双向星际航路构成,其中每颗行星都有一个不同的引力值.大赛要求车手们从一颗与这N颗行星之间没有任何航路的天体出发,访问这N颗行星每颗恰好一次,首先完成这一目标的人获得胜利.由于赛制非常开放,很多人驾驶着千奇百怪的自制赛

【bzoj4108】[Wf2015]Catering 有上下界费用流

原文地址:http://www.cnblogs.com/GXZlegend/p/6832537.html 题目描述 有一家装备出租公司收到了按照时间顺序排列的n个请求. 这家公司有k个搬运工.每个搬运工可以搬着一套装备按时间顺序去满足一些请求.一个搬运工从第i个请求的位置把东西搬到第j个请求的位置需要一些费用.公司的编号是1,请求的编号是2到n+1.所有搬运工必需从公司出发. 求满足所有请求所需的最小搬运费用. 输入 有可能有多组数据.(实际上没有) 第一行两个正整数n,k. 接下来n行,第i行

【bzoj2324】[ZJOI2011]营救皮卡丘 最短路-Floyd+有上下界费用流

原文地址:http://www.cnblogs.com/GXZlegend/p/6832504.html 题目描述 皮卡丘被火箭队用邪恶的计谋抢走了!这三个坏家伙还给小智留下了赤果果的挑衅!为了皮卡丘,也为了正义,小智和他的朋友们义不容辞的踏上了营救皮卡丘的道路. 火箭队一共有N个据点,据点之间存在M条双向道路.据点分别从1到N标号.小智一行K人从真新镇出发,营救被困在N号据点的皮卡丘.为了方便起见,我们将真新镇视为0号据点,一开始K个人都在0号点. 由于火箭队的重重布防,要想摧毁K号据点,必须

hdu 4862 Jump 上下界费用流

对于每个点拆点成为两个点a,b,连接a到b的上界为1,下界为1的边,保证用过一次且仅一次. 然后若点u可到达点v,则连接即可.建成了一个上下界网络,将下界拆出去,求最大费用最大流就好. #include <stdio.h> #include <iostream> #include <string.h> using namespace std; const int N=800; const int MAXE=200000; const int inf=1<<3

Acdream 1171 Matrix sum 上下界费用流

题目链接:点击打开链接 Matrix sum Time Limit: 8000/4000MS (Java/Others)Memory Limit: 128000/64000KB (Java/Others) SubmitStatisticNext Problem Problem Description sweet和zero在玩矩阵游戏,sweet画了一个N * M的矩阵,矩阵的每个格子有一个整数.zero给出N个数Ki,和M个数Kj,zero要求sweet选出一些数,满足从第 i 行至少选出了Ki

zoj 3231(上下界费用流)

题意:树上每个节点上有若干苹果,边上带权,问你最小费用使得书上的苹果方差最小. 思路:上下费用流问题,参考http://blog.csdn.net/qq564690377/article/details/8870587 代码如下: 1 /************************************************** 2 * Author : xiaohao Z 3 * Blog : http://www.cnblogs.com/shu-xiaohao/ 4 * Last m

【bzoj2055】80人环游世界 有上下界费用流

原文地址:http://www.cnblogs.com/GXZlegend 题目描述 想必大家都看过成龙大哥的<80天环游世界>,里面的紧张刺激的打斗场面一定给你留下了深刻的印象.现在就有这么 一个80人的团伙,也想来一次环游世界. 他们打算兵分多路,游遍每一个国家. 因为他们主要分布在东方,所以他们只朝西方进军.设从东方到西方的每一个国家的编号依次为1...N.假若第i个人的游历路线为P1.P2......Pk(0≤k≤N),则P1<P2<......<Pk. 众所周知,中