网络流模板(费用流/可行流/上下界)

终于来写博客了= =

一般的最大流
算法:dinic          博主只会这个
大概就是先bfs分层,在增广的时候只走跨层边
加当前弧优化好像快10倍左右,加的时候小心点,容易错
应该只有我会写成暴力吧
UPD:又打错了一次hhhhhhh    2017/7/15
struct edge{int t,f,n;}E[M];
void add(int x,int y,int f){
	E[++tot]=(edge){y,f,ft[x]},ft[x]=tot;
	E[++tot]=(edge){x,0,ft[y]},ft[y]=tot;
}
#define FOR for (int o=cu[u],v;v=E[o].t;o=E[o].n)
//写图论用的define		cu为当前弧 
void bfs(int u=S){
	memset(d,0,sizeof(d));
	memcpy(cu,ft,sizeof(cu));
	for (d[q[h=t=1]=u]=1;h<=t;u=q[++h])
		FOR if (E[o].f&&!d[v]) d[q[++t]=v]=d[u]+1;
}
int dfs(int u,int f){
	if (u==T||!f) return f;
	int g=0,t;
	FOR if (cu[u]=o,d[v]==d[u]+1&&(t=dfs(v,min(f,E[o].f))))
		g+=t,f-=t,E[o].f-=t,E[o^1].f+=t;
	return g;
}
int main(){
	int ans=0;
	while (bfs(),d[T]) dfs(S,1e8);
}

一般的费用流
算法:SPFA+EK          还是只会最朴素的一种T_T
每次贪心走费用最小的增广路径

bool SPFA(int u){
	memset(dis,0x7f,n+1<<3);
	unsigned short h=0,t=0;
	for (dis[q[t++]=u]=0;h!=t;){
		in[u=q[h++]]=0;
		FOR if (E[o].f&&dis[v]>dis[u]+E[o].c){
			dis[v]=dis[u]+E[o].c,pre[v]=o;
			if (!in[v]) if (in[q[t++]=v]=1,
				dis[v]<dis[q[h]]) swap(q[t-1],q[h]);	//SLF
		}
	}
	return dis[T]<1<<60;
}
int dfs(int u,int f){		//递归增广 
	if (u==S) return cst+=dis[T]*f,f;
	int t=dfs(E[pre[u]^1].t,min(f,E[pre[u]].f));
	E[pre[u]].f-=t,E[pre[u]^1].f+=t;
	return t;
}
int mincmf(){
	while (SPFA(S)) dfs(T,1e7);
	return cst;
}

最大费用可行流
有源汇就一直跑到流的费用为负为止
无源汇做法:强制费用为正的边满流,建立源汇来满足流量等式,源流向缺流的点,多流的点流向汇
最后把源汇互换,跑一边最小费用最大流,即把从源流来的流量退回去
答案就是正权边费用之和减去最小费用

void push(int x,int y,int f,int c){
	if (c>0){
		deg[x]+=f,deg[y]-=f,sum+=LL(f)*c;
		add(x,y,f,c);
	} else add(y,x,f,-c);
}
LL cal(){
	for (i=1;i<n-1;i++){
		if (deg[i]>0) add(S,i,deg[i],0);
		if (deg[i]<0) add(i,T,-deg[i],0);
	}
	return sum-mincmf();
}

上下界可行流
无源汇做法:强制流满下界,流量限制变为0~mx-mn,同上建立源汇满足流量等式
对源汇做最大流,如果跑不满新加的边则说明没有可行流,否则有
有源汇做法类似, 先从汇连一条上限INF的边到源,然后用无源汇做法消掉下界,就可以知道有没有可行流
若要求最大流,则用原本的源到汇做最大流
要求最小流则从汇到源做最大流,即尽可能退流

void lim(int x,int y,int l,int r,int c=0){
	cost+=1ll*c*l;		//这是上下界费用流的费用
	deg[x]-=l,deg[y]+=l;
	if (r!=l) add(x,y,r-l,c);
}

上下界费用流
无源汇做法:类似上下界可行流,消去下界时改为跑最小费用最大流
然后。。。好像有点不对劲
算了继续留坑=w=
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值