emm......先从上下界网络流(转)开始
再到现在的上下界费用流
因为有上下界,我们需要记下每个点的流量差$ex[i]$,用于调整
$ins(x,y,l,r,v)=link(x,y,r-l,v),ex[x]-=l,ex[y]+=l$
每个点都得经过$w$次,等价于:
把每个点$i$拆成$i_1,i_2$,$ins(i_1,i_2,w,w,0)$
题目限制总流量$=m$,于是设置一个附加点$T0$,$ins(T0,T,0,m,0)$,(最终一定会流满$m$)
对于其他点,也套路地连边:
$ins(S,i_1,0,m,0)$
$ins(i_2,T0,0,m,0)$
对于两点之间的边$(i,j,w)$:$ins(i_2,j_1,0,m,w)$
使源汇点流量守恒:$ins(T,S,0,m,0)$
蓝后就是对原图的调整(原图可能有些点不流量守恒):
按照上下界网络流的套路,新建调整源点汇点$pS,pT$
$if(ex[i]<0) link(i,pT,-ex[i],0);$
$if(ex[i]>0) link(pS,i,ex[i],0);$
最后直接以$pS,pT$为源点汇点跑费用流
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define N 2005
#define M 100005
int n,m,S,T0,T,pS,pT,ex[N],d[N],a[N],p[N],tC;
queue <int> h; bool inh[N];
int cnt=,hd[N],nxt[M],ed[N],poi[M],val[M],cst[M];
inline void adde(int x,int y,int v1,int v2){
nxt[ed[x]]=++cnt, hd[x]=hd[x]?hd[x]:cnt,
ed[x]=cnt, poi[cnt]=y, val[cnt]=v1,cst[cnt]=v2;
}
inline void link(int x,int y,int v1,int v2){adde(x,y,v1,v2),adde(y,x,,-v2);}
inline void ins(int x,int y,int l,int r,int v){link(x,y,r-l,v),ex[x]-=l,ex[y]+=l;}
bool bfs(){
memset(d,,sizeof(d)); int inf=d[];
h.push(S); d[S]=; a[S]=inf; inh[S]=;
while(!h.empty()){
int x=h.front(); h.pop(); inh[x]=;
for(int i=hd[x];i;i=nxt[i]){
int to=poi[i];
if(val[i]>&&d[to]>d[x]+cst[i]){
d[to]=d[x]+cst[i]; p[to]=i;
a[to]=min(a[x],val[i]);
if(!inh[to]) inh[to]=,h.push(to);
}
}
}if(d[T]==inf) return ;
tC+=a[T]*d[T];
for(int i=T;i!=S;i=poi[p[i]^])
val[p[i]]-=a[T],val[p[i]^]+=a[T];
return ;
}
int main(){
scanf("%d%d",&n,&m); int w;
S=*n+; T0=S+; T=T0+; pS=T+; pT=pS+;
ins(T0,T,,m,);
for(int i=;i<=n;++i){
ins(S,i,,m,);
ins(i+n,T0,,m,);
scanf("%d",&w);
ins(i,i+n,w,w,);
}
for(int i=;i<=n;++i)
for(int j=i+;j<=n;++j){
scanf("%d",&w);
if(w>-) ins(i+n,j,,m,w);
}
for(int i=;i<=T;++i){//调整
if(ex[i]<) link(i,pT,-ex[i],);
if(ex[i]>) link(pS,i,ex[i],);
}ins(T,S,,m,);
swap(S,pS);swap(T,pT);
while(bfs());
printf("%d",tC);
return ;
}