UVA11594 All Pairs Maximum Flow 题解

最大流 == 最小割,所以这是最小割树板子。

最小割树的做法是,每次在当前集合中选两个点跑最小割,然后在树上连边,权值为最小割容量。

众所周知最大流可以求出割的方案,即从源点开始,只走没流满的边,能去的点就是和源点在一起的。

把两个点集分别做上述步骤,类似分治,复杂度 O(n3m)O(n^3m)

然后,原图上 uuvv 的最小割即为树上两点路径上的边权最小值。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <climits>
using namespace std;

const int N = 205, M = N * N * 50;

int n, a[N][N], ans[N][N];
int t;
vector<pair<int, int> > NG[N];

class MaxFlow
{
public:
	bool vis[M];
	int e[M], h[M], ori[M], c[M], ne[M], idx;
	int d[M], cur[M], S, T;
	void CLEAR()
	{
		for (int i = 0; i <= idx; i++) e[i] = h[i] = ori[i] = c[i] = ne[i] = d[i] = cur[i] = 0;
		for (int i = 1; i <= n; i++) h[i] = -1, vis[i] = 0;
		idx = 0;
	}
	void add(int u, int v, int w)
	{
		e[idx] = v, c[idx] = ori[idx] = w, ne[idx] = h[u], h[u] = idx++;
		e[idx] = u, c[idx] = ori[idx] = 0, ne[idx] = h[v], h[v] = idx++;
	}
	bool bfs()
	{
		for (int i = 1; i <= n; i++) d[i] = cur[i] = -1;
		d[S] = 0;
		cur[S] = h[S];
		queue<int> q;
		q.push(S);
		while (q.size())
		{
			auto u = q.front();
			q.pop();
			for (int i = h[u]; ~i; i = ne[i])
			{
				auto j = e[i];
				if (d[j] == -1 && c[i] > 0)
				{
					d[j] = d[u] + 1;
					cur[j] = h[j];
					if (j == T) return 1;
					q.push(j);
				}
			}
		}
		return 0;
	}
	int dfs(int u, int lim)
	{
		if (u == T) return lim;
		int res = 0;
		for (int i = cur[u]; ~i && res < lim; i = ne[i])
		{
			cur[u] = i;
			auto j = e[i];
			if (d[j] == d[u] + 1 && c[i] > 0)
			{
				auto p = dfs(j, min(c[i], lim - res));
				if (!p) d[j] = -1;
				else
				{
					res += p;
					c[i] -= p;
					c[i ^ 1] += p;
				}
			}
		}
		return res;
	}
	int dinic(int s, int t)
	{
		S = s, T = t;
		int res = 0;
		while (bfs())
		{
			while (auto p = dfs(S, INT_MAX)) res += p;
		}
		return res;
	}
	void bfss(int s)
	{
		queue<int> q;
		q.push(s);
		vis[s] = 1;
		while (q.size())
		{
			auto u = q.front();
			q.pop();
			for (auto i = h[u]; ~i; i = ne[i])
			{
				auto j = e[i];
				if (vis[j] || ori[i] == 0 || c[i ^ 1] == ori[i]) continue;
				vis[j] = 1;
				q.push(j);
			}
		}
	}
}mf;

void solve(vector<int>& p)
{
	if (p.size() <= 1) return;
	mf.CLEAR();
	int s = p[0], t = p[1];
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			if (i != j)
			{
				mf.add(i, j, a[i][j]);
			}
		}
	}
	int ee = mf.dinic(s, t);
	NG[s].emplace_back(make_pair(t, ee));
	NG[t].emplace_back(make_pair(s, ee));
	mf.bfss(s);
	vector<int> v1, v2;
	for (auto& i : p)
	{
		(mf.vis[i] ? v1.emplace_back(i) : v2.emplace_back(i));
	}
	solve(v1);
	solve(v2);
	v1.clear(), v1.shrink_to_fit();
	v2.clear(), v2.shrink_to_fit();
}

void dfs(int u, int f, int fa, int res)
{
	ans[u][f] = res;
	for (auto& j : NG[u])
	{
		if (j.first != fa)
		{
			dfs(j.first, f, u, min(res, j.second));
		}
	}
}

int main()
{
	ios::sync_with_stdio(0), cin.tie(0);
	cin >> t;
	int c = 0;
	while (t--)
	{
		cin >> n;
		for (int i = 1; i <= n; i++)
		{
		    NG[i].clear();
			for (int j = 1; j <= n; j++) cin >> a[i][j];
		}
		vector<int> v;
		for (int i = 1; i <= n; i++) v.emplace_back(i);
		solve(v);
		for (int i = 1; i <= n; i++)
		{
			dfs(i, i, i, INT_MAX);
			ans[i][i] = 0;
		}
		cout << "Case #" << ++c << ":\n";
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j < n; j++) cout << ans[i][j] << " ";
			cout << ans[i][n] << "\n";
		}
	}
	return 0;
}
posted @   HappyBobb  阅读(19)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示