[网络流]最大流

网络流最大流最小割

题目链接

就是一道点割。

先说边割

边割比较常见。

最大流

最大流等于最小割,我懒得证。

求最大流的思路就是每次尝试找一条从源点到汇点的通路,然后找到这条路上残余流量最小的流量,答案加上这个流量,这条通路上每条边的残余流量减去这个值,反向边加上这个值。

关于反向边,实际上是一个反悔机制。反向流多少,表示正向已经流了多少。也就是说,如果我们从反向边流了一些流量,就相当于从这条边退回了一部分流量。

点割

所谓点割,就是被割掉的不再是边,而是点。思路是将点转化成边。

如上题:将每个点拆成\(i\)\(i+n\)两个点,中间连一条流量为\(1\)的边,将这条边割掉,就相当于割掉这个点。

详见代码。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
long long read(){
	long long x = 0; int f = 0; char c = getchar();
	while(c < '0' || c > '9') f |= c == '-', c = getchar();
	while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
	return f ? -x:x;
}

const int INF = 2147483647;
int n, m, s, t;
struct szh{
	int nxt, to, w;
}a[4004];
int head[203],cnt=1;
void add(int x, int y, int w){
	a[++cnt].nxt = head[x], a[cnt].to = y, head[x] = cnt, a[cnt].w = w;
}

int dis[203];
bool bfs(){
	memset(dis, 0, sizeof dis);
	queue<int> q;
	q.push(s), dis[s] = 1;
	while(!q.empty()){
		int u = q.front();q.pop();
		for (int i = head[u], v; v = a[i].to, i; i = a[i].nxt)
			if(a[i].w && !dis[v]) q.push(v), dis[v] = dis[u] + 1;
	}
	return dis[t];
}

int fir[103];
int dfs(int u, int flow){
	if(u == t || !flow) return flow;
	int ans = 0;
	for (int &i = fir[u], v; v = a[i].to, i; i = a[i].nxt)
        //弧优化
		if(a[i].w && dis[v] == dis[u] + 1){
			int x = dfs(v, min(flow, a[i].w));
			a[i].w -= x, a[i^1].w += x, ans += x; //更新残余流量
			if(!(flow-=x)) break;
		}
	return ans;
}

void dinic(){
	int ans = 0;
	while(bfs()){
		for (int i = 1; i <= (n << 1); ++i) fir[i] = head[i];
        //为dfs中取址操作做铺垫
		ans += dfs(s,INF);
	}
	printf("%d", ans);
}

int main(){
	n = read(), m = read(), s = read(), t = read();
    s += n; //源点不能删掉
	for (int i = 1; i <= n; ++i) add(i, i + n, 1), add(i + n, i, 0);
    //拆点
	for (int i = 1; i <= m; ++i){
		int x = read(), y = read();
		add(x + n, y, INF), add(y, x + n, 0); //电脑之间不会断,所以连INF
		add(y + n, x, INF), add(x, y + n, 0);
	}
	dinic(); //模板
	return 0;
}
posted @ 2019-03-24 20:47  Kylin_Seven  阅读(411)  评论(0编辑  收藏  举报