[AHOI2014]支線劇情(有上下界的網路流)
阿新 • • 發佈:2020-07-30
[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)); }