1. 程式人生 > 實用技巧 >[AHOI2014]支線劇情(有上下界的網路流)

[AHOI2014]支線劇情(有上下界的網路流)

[AHOI2014]支線劇情(有上下界的網路流)

題面

JYY現在所玩的RPG遊戲中,一共有N個劇情點,由1到N編號,第i個劇情點可以根據JYY的不同的選擇,而經過不同的支線劇情,前往Ki種不同的新的劇情點。當然如果為0,則說明i號劇情點是遊戲的一個結局了。

JYY觀看一個支線劇情需要一定的時間。JYY一開始處在1號劇情點,也就是遊戲的開始。顯然任何一個劇情點都是從1號劇情點可達的。此外,隨著遊戲的進行,劇情是不可逆的。所以遊戲保證從任意劇情點出發,都不能再回到這個劇情點。由於JYY過度使用修改器,導致遊戲的“存檔”和“讀檔”功能損壞了,

所以JYY要想回到之前的劇情點,唯一的方法就是退出當前遊戲,並開始新的遊戲,也就是回到1號劇情點。JYY可以在任何時刻退出遊戲並重新開始。不斷開始新的遊戲重複觀看已經看過的劇情是很痛苦,JYY希望花費最少的時間,看完所有不同的支線劇情。 \(N \leq 300\)

分析

看完所有劇情,就是要求每條邊至少經過一次。又因為時間可以疊加計算,容易想到有上下界的費用流。源點連向1號節點,出度為0的點連向匯點,流量上下界為$[0,\infin)\(.原圖上的邊上下界為\)[1,\infin)$即可。

有上下界的網路流見網路流常見建圖套路總結

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define maxn 100000 
#define maxm 500000 
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
namespace MCMF{
    struct edge{
        int from;
        int to;
        int next;
        ll flow;
        ll cost; 
    }E[maxm*2+5];
    int head[maxn+5];
    int esz=1;
    void add_edge(int u,int v,ll w,ll c){
//		printf("%d->%d vol=%lld cost=%lld\n",u,v,w,c); 
        esz++;
        E[esz].from=u;
        E[esz].to=v;
        E[esz].flow=w;
        E[esz].cost=c;
        E[esz].next=head[u];
        head[u]=esz;
        esz++;
        E[esz].from=v;
        E[esz].to=u;
        E[esz].flow=0;
        E[esz].cost=-c;
        E[esz].next=head[v];
        head[v]=esz;
    }
    
    ll dist[maxn+5],minf[maxn+5],last[maxn+5];
    bool inq[maxn+5];
    bool spfa(int s,int t){
        queue<int>q;
        memset(minf,0x3f,sizeof(minf)); 
        memset(dist,0x3f,sizeof(dist));
        memset(inq,0,sizeof(q));
        q.push(s);
        dist[s]=0;
        inq[s]=1;
        while(!q.empty()){
            int x=q.front();
            q.pop();
            inq[x]=0;
            for(int i=head[x];i;i=E[i].next){
                int y=E[i].to;
                if(E[i].flow){
                    if(dist[y]>dist[x]+E[i].cost){
                        dist[y]=dist[x]+E[i].cost;
                        minf[y]=min(minf[x],E[i].flow);
                        last[y]=i;
                        if(!inq[y]){
                            inq[y]=1;
                            q.push(y);
                        }
                    }
                }
            }
        }
        if(dist[t]==INF) return 0;
        else return 1;
    }
    
    void update(int s,int t){
        int x=t;
        while(x!=s){
            int i=last[x];
            E[i].flow-=minf[t];
            E[i^1].flow+=minf[t];
            x=E[i].from;
        }
    }
    ll mcmf(int s,int t){
        ll ct=0;
        while(spfa(s,t)){
            update(s,t);
            ct+=dist[t]*minf[t];
        }
        return ct;
    }
} 
namespace EXMCMF{
    struct _edge{
        int from;
        int to;
        ll lflow;
        ll rflow;
        ll cost; 
    }E[maxm+5];	
    int cnte=0,cntv=0;
    void adde(int u,int v,ll l,ll r,ll c){
        cnte++;
        cntv=max(max(u,v),cntv);
        E[cnte].from=u;
        E[cnte].to=v;
        E[cnte].lflow=l;
        E[cnte].rflow=r;
        E[cnte].cost=c;
    }
    ll dflow[maxn+5];
    ll solve(int s,int t){
        ll ans=0;
        int ss=cntv+1,tt=cntv+2;
        adde(t,s,0,INF,0);
        for(int i=1;i<=cnte;i++){
            dflow[E[i].from]-=E[i].lflow;
            dflow[E[i].to]+=E[i].lflow;
            ans+=E[i].lflow*E[i].cost;
            MCMF::add_edge(E[i].from,E[i].to,E[i].rflow-E[i].lflow,E[i].cost); 
        }
        for(int i=0;i<=cntv;i++){
            if(dflow[i]>0) MCMF::add_edge(ss,i,dflow[i],0);
            else if(dflow[i]<0) MCMF::add_edge(i,tt,-dflow[i],0);
        }
        return ans+MCMF::mcmf(ss,tt);
    }
}

int n;
int main(){
    int k,w,v;
    scanf("%d",&n);
    int s=0,t=n+1;
    EXMCMF::adde(s,1,0,INF,0);
    for(int i=1;i<=n;i++){
        scanf("%d",&k);
        for(int j=1;j<=k;j++){
            scanf("%d %d",&v,&w);
            EXMCMF::adde(i,v,1,INF,w);
        }
        EXMCMF::adde(i,t,0,INF,0);
    }
    printf("%lld\n",EXMCMF::solve(s,t)); 
}