BZOJ3876: [Ahoi2014&Jsoi2014]支线剧情
Description
【故事背景】
宅男JYY非常喜欢玩RPG游戏,比如仙剑,轩辕剑等等。不过JYY喜欢的并不是战斗场景,而是类似电视剧一般的充满恩怨情仇的剧情。这些游戏往往
都有很多的支线剧情,现在JYY想花费最少的时间看完所有的支线剧情。
【问题描述】
JYY现在所玩的RPG游戏中,一共有N个剧情点,由1到N编号,第i个剧情点可以根据JYY的不同的选择,而经过不同的支线剧情,前往Ki种不同的新的剧情点。当然如果为0,则说明i号剧情点是游戏的一个结局了。
JYY观看一个支线剧情需要一定的时间。JYY一开始处在1号剧情点,也就是游戏的开始。显然任何一个剧情点都是从1号剧情点可达的。此外,随着游戏的进行,剧情是不可逆的。所以游戏保证从任意剧情点出发,都不能再回到这个剧情点。由于JYY过度使用修改器,导致游戏的“存档”和“读档”功能损坏了,
所以JYY要想回到之前的剧情点,唯一的方法就是退出当前游戏,并开始新的游戏,也就是回到1号剧情点。JYY可以在任何时刻退出游戏并重新开始。不断开始新的游戏重复观看已经看过的剧情是很痛苦,JYY希望花费最少的时间,看完所有不同的支线剧情。
Input
输入一行包含一个正整数N。
接下来N行,第i行为i号剧情点的信息;
第一个整数为,接下来个整数对,Bij和Tij,表示从剧情点i可以前往剧情点,并且观看这段支线剧情需要花费的时间。
Output
输出一行包含一个整数,表示JYY看完所有支线剧情所需要的最少时间。
Sample Input
6
2 2 1 3 2
2 4 3 5 4
2 5 5 6 6
0
0
0
2 2 1 3 2
2 4 3 5 4
2 5 5 6 6
0
0
0
Sample Output
24
HINT
JYY需要重新开始3次游戏,加上一开始的一次游戏,4次游戏的进程
1->2->4,1->2->5,1->3->5和1->3->6。
对于100%的数据满足N<=300,0<=Ki<=50,1<=Tij<=300,Sigma(Ki)<=5000
题解Here!
首先应该能发现这是个费用流的题。。。
因为要把整个图走一遍,还有时间,于是费用流没得跑。
走一次路径会给这条路径上的所有边增加$1$的流量,再给所有边赋一个费用等于边权。
这样,我们只要设每条边的流量有一个$1$的下限,上限为无限大,就能做了。
还要把所有的剧情结束点(没有出边的)连到一个超级汇点,源点就是$1$号点。
跑一个最小费用可行流即可。
什么?你不知道最小费用可行流?
教程
考虑一张网络流图,每条边定义为$(u,v,l,r,cost)$,代表从$u$到$v$的一条有向边,流量为$[l,r]$闭区间,费用为$cost$,源点$s$汇点$t$已知,且保证源点没有入边、汇点没有出边。
同时定义常规费用流图的边为$(u,v,w,cost)$
现在我们需要求这张图的最小费用可行流,就是满足所有边的流量上下限制,同时费用最小。
按照如下方式建立附加边和附加点:
- 建立附加源点$S$,和附加汇点$T$
- 对于原图中每一个点,包括源汇点$s,t$,u,令$degree[u]$代表$u$点的所有入边的流量下界减去出边的流量下界。
然后有两种情况:
- 如果$degree[u]<0$,那么从$u$连一条边到$T$,即$(u,T,-degree[u],0)$
- 如果$degree[u]>0$,那么从$S$连一条边到$u$,即$(S,u,degree[u],0)$
3. 对于原图中每一条边$(u,v,l,r,cost)$,连边$(u,v,r-l,cost)$。
4. 连边$(t,s,MAX,0)$。注意这里是原图的源汇点!不是附加的源汇点!
这样以后,从$S$到$T$跑新图的最小费用最大流,再加上原图中每条边的下界流量乘以费用(必须跑的部分),就是最小费用可行流的费用了。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<queue> #define MAXN 310 #define MAXM 5010 #define MAX 999999999 using namespace std; int n,m,s,t,S,T,c=2,maxflow=0,mincost=0,sum=0; int head[MAXN],deep[MAXN],flow[MAXN],path[MAXN],fa[MAXN],degree[MAXN]; bool vis[MAXN]; struct Edge{ int next,to,w,cost; }a[MAXM<<2]; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } inline int relax(int u,int v,int i,int w,int cost){ if(path[v]>path[u]+cost){ path[v]=path[u]+cost; fa[v]=u; deep[v]=i; flow[v]=min(flow[u],w); return 1; } return 0; } inline void add(int u,int v,int w,int cost){ a[c].to=v;a[c].w=w;a[c].cost=cost;a[c].next=head[u];head[u]=c++; a[c].to=u;a[c].w=0;a[c].cost=-cost;a[c].next=head[v];head[v]=c++; } bool spfa(){ int u,v; queue<int> q; for(int i=S;i<=T;i++){path[i]=MAX;vis[i]=false;deep[i]=0;fa[i]=-1;} path[S]=0; vis[S]=true; flow[S]=MAX; q.push(S); while(!q.empty()){ u=q.front(); q.pop(); vis[u]=false; for(int i=head[u];i;i=a[i].next){ v=a[i].to; if(a[i].w&&relax(u,v,i,a[i].w,a[i].cost)&&!vis[v]){ vis[v]=true; q.push(v); } } } if(path[T]==MAX)return false; return true; } void EK(){ while(spfa()){ for(int i=T;i!=S;i=fa[i]){ a[deep[i]].w-=flow[T]; a[deep[i]^1].w+=flow[T]; } maxflow+=flow[T]; mincost+=flow[T]*path[T]; } } void work(){ EK(); printf("%d ",sum+mincost); } void init(){ int x,y,k; n=read(); s=1;t=n+1;S=0;T=n+2; for(int i=1;i<=n;i++){ k=read(); degree[i]-=k; add(i,t,MAX,0); for(int j=1;j<=k;j++){ x=read();y=read(); degree[x]++; sum+=y; add(i,x,MAX,y); } } for(int i=1;i<=n;i++){ if(degree[i]<0)add(i,T,-degree[i],0); if(degree[i]>0)add(S,i,degree[i],0); } add(t,s,MAX,0); } int main(){ init(); work(); return 0; }