網絡最大流【Dinic算法】

網絡最大流

題目鏈接:luogu P3376

題目

如題,給出一個網絡圖,以及其源點和匯點,求出其網絡最大流。

輸入

第一行包含四個正整數NNMMSSTT,分別表示點的個數、有向邊的個數、源點序號、匯點序號。
接下來MM行每行包含三個正整數uiuiviviwiwi,表示第i條有向邊從uiui出發,到達vivi,邊權爲wiwi(即該邊最大流量爲wiwi

輸出

一行,包含一個正整數,即爲該網絡的最大流。

樣例輸入

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

樣例輸出

50

樣例解釋

在這裏插入圖片描述
題目中存在33條路徑:

  1. 路徑爲:4>2>34-->2-->3,該路線可通過2020的流量
  2. 路徑爲:4>34-->3,可通過2020的流量
  3. 路徑爲:4>2>1>34-->2-->1-->3,可通過1010的流量(邊4>24-->2之前已經耗費了2020的流量)

故流量總計20+20+10=50420+20+10=504。輸出5050

數據範圍

對於30%30\%的數據:N<=10N<=10M<=25M<=25
對於70%70\%的數據:N<=200N<=200M<=1000M<=1000
對於100%100\%的數據:N<=10000N<=10000M<=100000M<=100000

思路

這是一道非常經典的模板題,我們這裏用DinicDinic算法來做。

其實就是把FordFulkersonFord-Fulkerson算法的找增廣路徑和dfsdfs增廣分開來,並在dfsdfs時進行了優化。

那優化是怎麼樣的呢?
我們在bfsbfs找增廣路徑的時候,我們可以將記錄這個點是否被走過的boolbool數組改成intint。這樣我們除了表示它有沒有被走過,還可以表示由源點到這個點走了多遠。
接着,我們在dfsdfs增廣的時候,我們就在某一個點的時候,我們就只找這個值剛好比他大11的點走。這樣,走出我們的增廣路就快了很多了。
(我這裏記錄的變量名叫disdis

這樣,我們就實現了優化。

代碼

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define rr register

using namespace std;

struct note {
	int to, go, op, next;
}e[200001];
int n, m, s, t, le[200001], x, y, z, k, dis[10001], ans;
queue <int> q;

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

bool bfs() {
	while (!q.empty()) q.pop();//清空隊列
	memset(dis, 0x7f, sizeof(dis));//初始化
	dis[s] = 0;//初始化
	
	q.push(s);
	while (!q.empty()) {
		int now = q.front();
		q.pop();
		
		for (rr int i = le[now]; i; i = e[i].next)
			if (dis[e[i].to] > dis[now] + 1 && e[i].go) {//沒到過且可以灌水
				dis[e[i].to] = dis[now] + 1;
				if (e[i].to == t) return 1;//到匯點了
				q.push(e[i].to);
			}
	}
	
	return 0;
}

int dfs(int now, int an) {
	if (now == t) return an;//到匯點了
	
	int go = 0;
	for (rr int i = le[now]; i; i = e[i].next)
		if (dis[e[i].to] == dis[now] + 1 && e[i].go) {
			int line_go = dfs(e[i].to, min(e[i].go, an - go));//這條增廣路最多能流多少
			if (!line_go) dis[e[i].to] = -1;//之前找到的不是這條路
			e[i].go -= line_go;//流
			e[e[i].op].go += line_go;//反向記錄
			
			go += line_go;//總流量
			if (go == an) break;//已經全部找完了
		}
	
	return go;
}

void dinic() {
	while (bfs())//還有增廣路徑
		ans += dfs(s, 2147483647);//dfs加最大流
}

int main() {
	n = read();//讀入
	m = read();
	s = read();
	t = read();
	
	for (rr int i = 1; i <= m; i++) {
		x = read();//讀入
		y = read();
		z = read();
		e[++k] = (note){y, z, k + 1, le[x]}; le[x] = k;//建鄰接表(正向)
		e[++k] = (note){x, 0, k - 1, le[y]}; le[y] = k;//建鄰接表(反向)
	}
	
	dinic();//dinic算法
	
	printf("%d", ans);//輸出
	
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章