【BZOJ-3876】支线剧情 有上下界的网络流(有下界有源有汇最小费用流)

3876: [Ahoi2014]支线剧情

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 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

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

By 佚名上传

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
",MinCost);
    return 0;
}
原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5470692.html