最小费用流 SPFA 多路增广

一直都只会费用流的暴力增广,但这次被卡住了:在一个二分图上暴力增广,增广的次数至少也是 O(n) 级别的,也就是说需要做 O(n) 次 SPFA 。这样写还不如写搜索划算。

多路增广怎么做呢?其实其过程很像 dinic :每次求出一个最短路网,然后对于其上的节点用 dfs 增广。每次增广完的路径上的节点要求将其标记清零:因为其有可能再次出现在最短路网中。接着像 dinic 一样:找到增广路上最早的一个有流量的点,继续重复以上过程增广即可。

虽然这道题还是只有 80 分(二分图最佳匹配,满分算法好像是 KM)。但是相对于暴力增广的 40 分来说:暴力增广一次只能增广不到一百的费用,而多路增广一次可以增广最大费用 39303 的费用(其十分之九),说明效率提升还是很大的。

Code : 

#include 
#include 
#include 
#include 

#define maxn 451
#define maxs 905
#define maxm 400005

#define ppp(e) (ve + (e - ve ^ 1))

int n, m, cnt, S, T, Q, co, h, t, tt;
int x[maxm], y[maxm], xx[maxm], yy[maxm], ans[maxm], dis[maxs], q[maxs], pre[maxs], vst[maxs];
int a[maxn][maxn];
bool b[maxn][maxn];
char ss[10];

struct da{int t, w, f; da * n;} ve[maxm], * adj = ve + 1, * edge[maxs], * prev[maxs];

inline void link(int i, int j, int k)
{
	* (++ adj) = (da) {j,   k, 1, edge[i]}, edge[i] = adj;
	* (++ adj) = (da) {i, - k, 0, edge[j]}, edge[j] = adj;
}

bool spfa()
{
	memset(dis, 0xEE, T + 1 << 2), dis[S] = q[S] = 0, ++ tt;
	for (h = t = S; h; vst[h] = 0, h = q[h])
		for (da * e = edge[h]; e; e = e->n)
			if (e->f && dis[h] + e->w > dis[e->t])
				if (dis[e->t] = dis[h] + e->w, vst[e->t] != tt)
					vst[e->t] = tt, q[e->t] = 0, t = q[t] = e->t;
	return dis[T] >= 0;
}

inline void extend()
{
	co += dis[T];
	for (int i = T; prev[i]; i = pre[i])
		prev[i]->f = 0, ppp(prev[i])->f = 1;
}

bool dfs(int u)
{
	if (u == T) {extend(); return 1;}
	else vst[u] = tt;
	for (da * e = edge[u]; e; e = e->n)
		if (vst[e->t] != tt && e->f && dis[u] + e->w == dis[e->t])
		{
			pre[e->t] = u, prev[e->t] = e;
			if (dfs(e->t) && u != S) {vst[u] = 0; return 1;}
		}
	return 0;
}

int main()
{
	freopen("c.in", "r", stdin);
	freopen("b.out", "w", stdout);

	scanf("%d%d%d", & n, & m, & cnt);
	S = n + m + 1, T = S + 1;
	for (int i = 1; i <= cnt; ++ i)
		scanf("%d%d", x + i, y + i), scanf("%d", & a[x[i]][y[i]]);
	scanf("%d", & Q);
	for (int i = 1; i <= Q; ++ i)
		if (scanf("%s", ss), * ss == 'A') xx[i] = yy[i] = - 1;
		else scanf("%d%d", xx + i, yy + i), b[xx[i]][yy[i]] = 1;
	for (int i = 1; i <= n; ++ i) link(S, i, 0);
	for (int i = 1; i <= m; ++ i) link(n + i, T, 0);
	for (int i = 1; i <= cnt; ++ i)
		if (! b[x[i]][y[i]]) link(x[i], n + y[i], a[x[i]][y[i]]);
	for (int i = Q; i; -- i)
		if (~ xx[i]) link(xx[i], n + yy[i], a[xx[i]][yy[i]]);
		else
		{
			co = 0;
			for (da * e = ve + 2; e <= adj; ++ e) e->f = 1, (++ e)->f = 0;
			while (spfa()) ++ tt, dfs(S);
			ans[i] = co;
		}
	for (int i = 1; i <= Q; ++ i)
		if (xx[i] == - 1) printf("%d\n", ans[i]);

	return 0;
}


你可能感兴趣的:(最小费用流 SPFA 多路增广)