#有源汇上下界最小费用可行流#洛谷 4043 [AHOI2014/JSOI2014] 支线剧情
分析
考虑从剧情点 \(i\) 到另一个剧情点 \(j\) 的边实际是费用为 \(t_{i,j}\),下界为 \(1\)(每条边都要经过一遍)
然后回到剧情点可以开汇点使得除了 \(1\) 号剧情点其它都能读回 \(1\) 号点,也就是第 \(i\) 个点向汇点连接费用为 \(0\),流量无上下界
那么就是有源汇上下界可行流,首先让汇点连向源点一条费用为 \(0\),流量无上下界的边转化为无源汇上下界可行流
那也就是考虑上下界开新的源汇点,遵守流量平衡后跑最小费用可行流即可。
代码
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int N=311,inf=0x3f3f3f3f;
struct node{int y,w,f,next;}e[N*N];
int dis[N],as[N],As[N],dep[N],v[N],n,et=1,_S,_T,S,T,ans,ANS,ouf[N];
void add(int x,int y,int w,int f){
e[++et]=(node){y,w,f,as[x]},as[x]=et;
e[++et]=(node){x,0,-f,as[y]},as[y]=et;
}
bool spfa(){
for (int i=1;i<=T;++i) dis[i]=inf,v[i]=0,dep[i]=0,As[i]=as[i];
queue<int>q; dis[T]=0,v[T]=1,q.push(T);
while (!q.empty()){
int x=q.front(); q.pop();
for (int i=as[x];i;i=e[i].next)
if (e[i^1].w&&dis[e[i].y]>dis[x]-e[i].f){
dis[e[i].y]=dis[x]-e[i].f,dep[e[i].y]=dep[x]+1;
if (!v[e[i].y]) v[e[i].y]=1,q.push(e[i].y);
}
v[x]=0;
}
return dis[S]<inf;
}
int dfs(int x,int now){
if (x==T) {v[T]=1; return now;}
int rest=0,f; v[x]=1;
for (int &i=As[x];i;i=e[i].next)
if (!v[e[i].y]&&e[i].w&&dep[e[i].y]==dep[x]-1&&dis[e[i].y]+e[i].f==dis[x]){
rest+=(f=dfs(e[i].y,min(e[i].w,now-rest)));
if (f) ANS+=f*e[i].f,e[i].w-=f,e[i^1].w+=f;
if (rest==now) break;
}
return rest;
}
int maxflow(){
int ans=0;
while (spfa()){
v[T]=1;
while (v[T]){
memset(v,0,sizeof(v));
ans+=dfs(S,inf);
}
}
return ans;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n,_S=1,_T=n+1,S=_T+1,T=S+1;
for (int i=1;i<=n;++i){
int m; cin>>m;
for (int j=1;j<=m;++j){
int x,f; cin>>x>>f;
add(i,x,inf-1,f),--ouf[x],++ouf[i],ANS+=f;
}
}
for (int i=2;i<=n;++i) add(i,_T,inf,0);
for (int i=1;i<=n;++i)
if (ouf[i]>0) add(i,T,ouf[i],0),ans+=ouf[i];
else if (ouf[i]<0) add(S,i,-ouf[i],0);
add(_T,_S,inf,0),maxflow();
cout<<ANS;
return 0;
}

浙公网安备 33010602011771号