【BZOJ-3876】支线剧情 有上下界的网络流(有下界有源有汇最小费用流)
3876: [Ahoi2014]支线剧情
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 821 Solved: 502
[Submit][Status][Discuss]
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
Source
Solution
有下界的有源有汇最小费用最大流,比较的裸
对于边u-->v连上界inf费用c,下界1费用c
对于每个点,连汇容量为出度,费用0;连1,代替源汇,容量inf,费用c
PS:zkw跑的飞快,不过rank前两页怎么会那么快......不是一个复杂度级的啊....
Code
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> using namespace std; int read() { int x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-')f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } #define maxn 310 #define maxm 1000100 int n; struct EdgeNode{int next,to,cap,cost;}edge[maxm]; int head[maxn],cnt=1; void add(int u,int v,int w,int c) { cnt++; edge[cnt].cap=w;edge[cnt].cost=c;edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt; } void insert(int u,int v,int w,int c) {add(u,v,w,c); add(v,u,0,-c);} int dis[maxn],S,T,MinCost; bool mark[maxn]; #define inf 0x7fffffff bool spfa() { queue<int>q; memset(mark,0,sizeof(mark)); for (int i=S; i<=T; i++) dis[i]=inf; q.push(T); dis[T]=0; mark[T]=1; while (!q.empty()) { int now=q.front(); q.pop(); mark[now]=0; for (int i=head[now]; i; i=edge[i].next) if (edge[i^1].cap && dis[edge[i].to]>dis[now]+edge[i^1].cost) { dis[edge[i].to]=dis[now]+edge[i^1].cost; if (!mark[edge[i].to]) q.push(edge[i].to),mark[edge[i].to]=1; } } return dis[S]!=inf; } int dfs(int loc,int low) { mark[loc]=1; if (loc==T) return low; int w,used=0; for (int i=head[loc]; i; i=edge[i].next) if (edge[i].cap && !mark[edge[i].to] && dis[edge[i].to]==dis[loc]-edge[i].cost) { w=dfs(edge[i].to,min(low-used,edge[i].cap)); edge[i].cap-=w; edge[i^1].cap+=w; used+=w; MinCost+=w*edge[i].cost; if (used==low) return low; } return used; } int zkw() { int tmp=0; while (spfa()) { mark[T]=1; while (mark[T]) memset(mark,0,sizeof(mark)),tmp+=dfs(S,inf); } return tmp; } int main() { n=read(); S=0,T=n+1; for (int i=1; i<=n; i++) { int m=read(); for (int v,c,j=1; j<=m; j++) v=read(),c=read(),insert(i,v,inf,c),insert(S,v,1,c); insert(i,T,m,0); if (i!=1) insert(i,1,inf,0); } zkw(); printf("%d\n",MinCost); return 0; }
——It's a lonely path. Don't make it any lonelier than it has to be.