[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));
}
版权声明:因为我是蒟蒻,所以请大佬和神犇们不要转载(有坑)的文章,并指出问题,谢谢