最小費用最大流

最小費用最大流

題目鏈接:luogu P3381

題目

如題,給出一個網絡圖,以及其源點和匯點,每條邊已知其最大流量和單位流量費用,求出其網絡最大流和在最大流情況下的最小費用。

輸入

第一行包含四個正整數NNMMSSTT,分別表示點的個數、有向邊的個數、源點序號、匯點序號。

接下來MM行每行包含四個正整數uiu_iviv_iwiw_ifif_i,表示第ii條有向邊從uiu_i出發,到達viv_i,邊權爲wiw_i(即該邊最大流量爲wiwi),單位流量的費用爲fif_i

輸出

一行,包含兩個整數,依次爲最大流量和在最大流量情況下的最小費用。

樣例輸入

4 5 4 3
4 2 30 2
4 3 20 3
2 3 20 1
2 1 30 9
1 3 40 5

樣例輸出

50 280

樣例解釋

在這裏插入圖片描述
如圖,最優方案如下:

第一條流爲4>34-->3,流量爲2020,費用爲320=603*20=60

第二條流爲4>2>34-->2-->3,流量爲2020,費用爲2+120=60(2+1)*20=60

第三條流爲4>2>1>34-->2-->1-->3,流量爲1010,費用爲2+9+510=160(2+9+5)*10=160

故最大流量爲5050,在此狀況下最小費用爲60+60+160=28060+60+160=280

故輸出50 28050\ 280

數據範圍

對於30%30\%的數據:N<=10N<=10M<=10M<=10
對於70%70\%的數據:N<=1000N<=1000M<=1000M<=1000
對於100%100\%的數據:N<=5000N<=5000M<=50000M<=50000

思路

這道題是一道模板題。
做法就是用SPFASPFA求增廣路徑,然後增廣,就可以了。
其實就是在最大流上面多了個要求最短路徑。
不會最大流的看這裏:——>點這裏<——

代碼

#include<queue>
#include<cstdio>
#include<cstring>
#define min(x, y) (x) < (y) ? (x) : (y)

using namespace std;

struct note {
	int to, now, pri, next, op;
}e[100001];
int n, m, s, t, le[5001], x, y, w, f, k, ans1, ans2, dis[5001], run[5001], pre[5001];
bool in[5001];
queue<int>q;

void add(int x, int y, int w, int f) {//連邊
	e[++k] = (note){y, w, f, le[x], k + 1}; le[x] = k;
	e[++k] = (note){x, 0, -f, le[y], k - 1}; le[y] = k; 
}

void getan() {
	int now = t;
	while (now != s) {//從匯點開始返回原點
		e[pre[now]].now -= run[t];//更改邊的值
		e[e[pre[now]].op].now += run[t];
		now = e[e[pre[now]].op].to;
	}
	
	ans1 += run[t];//加到答案上面
	ans2 += run[t] * dis[t];
}

bool spfa() {
	memset(dis, 0x7f, sizeof(dis));
	memset(pre, 0, sizeof(pre));
	memset(run, 0, sizeof(run));
	memset(in, 0, sizeof(in));
	while (!q.empty()) q.pop();
	int temp = dis[0];
	in[s] = 1;
	dis[s] = 0;
	run[s] = 2147483647;
	
	q.push(s);
	while (!q.empty()) {
		int now = q.front();
		q.pop();
		
		for (int i = le[now]; i; i = e[i].next)
			if (dis[now] + e[i].pri < dis[e[i].to] && e[i].now) {//費用更小且可以流
				dis[e[i].to] = dis[now] + e[i].pri;
				run[e[i].to] = min(run[now], e[i].now);//維護這條路最多能留多少
				pre[e[i].to] = i;//記錄編號,以便之後返回
				
				if (!in[e[i].to]) {
					in[e[i].to] = 1;
					q.push(e[i].to);
				}
			}
		
		in[now] = 0;
	}
	
	if (dis[t] == temp) return 0;
	return 1;
}

int read() {//快讀
	int an = 0;
	char c = getchar();
	while (c > '9' || c < '0') c = getchar();
	while (c <= '9' && c >= '0') {
		an = an * 10 + c - 48;
		c = getchar();
	}
	return an;
}

int main() {
	n = read();//輸入
	m = read();
	s = read();
	t = read();
	
	for (int i = 1; i <= m; i++) {
		x = read();//輸入
		y = read();
		w = read();
		f = read();
		add(x, y, w, f);//連邊
	}
	
	while (spfa())//spfa求最小費最大流
		getan();//增值
	
	printf("%d %d", ans1, ans2);//輸出
	
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章