最小费用最大流模版

#include <cstdio>
#include <algorithm>
#include <cstring>
//思路 : 不断找最短路, 直到不能增广了则为最小费用最大流 
using namespace std;
int n, m, s, t;
struct node{
	int a, b, c, n, v;
}d[200001];
int h[10001];
int v[10001];//费用 
int w[10001];//容量 
int u[10001];
int que[100001];
int pre[10001];//前导边
int bian[100001];
int ans; 
int read(){
	int x, f = 1;
	char ch;
	while(ch = getchar(), ch<'0'||ch>'9') if(ch == '-') f = -1;
	x = ch - 48;
	while(ch = getchar(), '0'<=ch&&ch<='9') x = x * 10 + ch - 48;
	return x * f;
}
void cr(int cnt, int a, int b, int c, int v){
	d[cnt].a = a; d[cnt].b = b; d[cnt].c = c; d[cnt].v = v; d[cnt].n = h[a];
	h[a] = cnt;
}
int cmp(int i, int j){
	 return d[i].v<d[j].v;
} 
void bfs(){
	memset(w,0,sizeof(v));
	memset(v,1,sizeof(v));
	memset(pre,0,sizeof(pre));
	w[s] = 1e9; v[s] = 0;
	int x, y, i, b, c, va, a, j;
	que[x = y = 1] = s;  
	while(x <= y){
		a = que[x++];
		u[a] = 0; 
		for(j = h[a]; j < h[a+1]; j++){
			i = bian[j];//一条条排完序的边出来 
			b = d[i].b;
			c = d[i].c;
			va = d[i].v;
			if(c>0 && v[a]+va<v[b]){//费用小才入队 
				v[b] = v[a] + va;
				pre[b] = i;
				w[b] = w[a]<c?w[a]:c;
				if(!u[b]){//spfa加sfl优化, 如果不在队列中,如果比目前对头的费用小就放在队头,不然放队尾, 可以减少入队 
				//可以 
					u[b] = 1;
					if(v[b]<v[que[x]] && x > 1) que[--x] = b;
					else que[++y] = b; 
				}
			}
		}
	}
	for(i = pre[t]; i; i = pre[d[i].a]){
		d[i].c -= w[t];
		d[i^1].c += w[t];//正向边反向边更新 
	}
}
int main(){
	int i, j, a, b, c, x, a1 = 0, k;
	n = read(); m = read(); s = read(); t = read();
	for(i = 1; i <= m; i++){
		a = read(); b = read(); c = read(); x = read();
		cr(j=i*2,a,b,c,x);
		cr(j^1,b,a,0,-x);
	}
	for(k=i=1; i <= n; i++){//人为的边排序, 把每一条边放入bian[]中 
		for(j = h[i], h[i] = k; j; j = d[j].n){
			bian[k++] = j;
		} 
	}
	h[n+1] = k;
	for(i = 1; i <= n; i++){
		if(h[i] < h[i+1]){
			sort(bian+h[i],bian+h[i+1],cmp);
		}
	}
	//权值小的边先出来,这样同一起点的边的终点可以不重复入队 
	while(1){
		bfs();
		if(w[t] > 0){
			a1 += w[t];
			ans += v[t] * w[t];
		}
		else break;
	}
	printf("%d %d", a1, ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值