[Notes] 网络流扩展模型

Dinic 代码

int n, m, in[N], out[N];
struct Edge { int to, next, cap; } e[M];
int head[N], cur[N], cnt;
I void add(int u, int v, int cap) {
	e[cnt] = Edge{v, head[u], cap};
	head[u] = cnt++;
}
I void addedge(int u, int v, int cap) {
	add(u, v, cap); add(v, u, 0);
}
int dis[N];
queue<int> q;
bool bfs(int s, int t) {
	memset(dis, -1, sizeof dis);
	memcpy(cur, head, sizeof head);
	dis[s] = 0;
	q.push(s);
	while (!q.empty()) {
		int u = q.front();
		q.pop();
		for (int i = head[u]; ~i; i = e[i].next) {
			int v = e[i].to;
			if (~dis[v] || e[i].cap == 0) continue;
			dis[v] = dis[u]+1;
			q.push(v);
		}
	}
	return dis[t] != -1;
}
int augment(int u, int t, int inflow) {
	if (u == t) return inflow;
	int outflow = 0;
	for (int i = cur[u]; ~i && inflow-outflow; i = e[i].next) {
		int v = e[i].to; cur[u] = i;
		if (e[i].cap > 0 && dis[v] == dis[u]+1) {
			int flow = augment(v, t, min(inflow-outflow, e[i].cap));
			if (flow) {
				e[i].cap -= flow;
				e[i^1].cap += flow;
				outflow += flow;
			}
		}
	}
	return outflow;
}
int dinic(int s, int t) {
	int maxflow = 0, x = 0;
	while (bfs(s, t)) {
		while ((x = augment(s, t, INF))) maxflow += x;
	}
	return maxflow;
}

最大权闭合子图

定义

给定有向图 \(G=(V,E)\),点有点权,边无边权,选择点集 \(V'\subseteq V\),设 \(V'\) 内点的所有出边构成集合 \(E'\),若子图 \(G'=(V',E')\) 构成 \(G\)导出子图 \(G[V']\),且点集 \(V'\) 的权值和 \(W=\sum_{u\in V'}w_u\) 最大,称子图 \(G'\) 为图 \(G\)最大权闭合子图

算法

如果有向图中有环需 \(\text{toposort}\) 处理掉。

转换为最小割模型。

连边:

\[\begin{cases} s\rightarrow i: cap=w_i & w_i \geq 0,\\ i\rightarrow t: cap =-w_i & \text{otherwise.} \end{cases} \]

\(\text{DAG}\) 上的边保留并将容量设置为 \(\text{INF}\)

\(sum=\sum_{i=1}^{n}w_i\cdot[w_i\geq 0]\),答案即为 \(rst=sum-maxflow(s,t)\)

最大密度子图

定义

给定无向图 \(G=(V,E)\),选择点集 \(V'\subseteq V\),对于 \(G\)导出子图 \(G[V']=(V',E')\),若子图密度 \(g=\frac{|E'|}{|V'|}\) 最大,则称子图 \(G[V']\) 为图 \(G\)最大密度子图

算法

由于密度为分数,首先考虑分数规划。二分密度 \(g'\),定义 \(h(g')=\max_{V'\subseteq V}(|E'|-g'\cdot |V'|)\),若 \(h(g')>0\),则 \(g>g'\);反之,\(g\leq g'\)

方法一

最粗暴的想法即对二分后的问题转为最大权闭合子图求解。一条边选择与否取决于两个端点,因此对边建点,按照依赖关系连向两个端点。

方法二

依然考虑转换为最小割模型,这样则需要将 \(|E'|\)割边数量建立联系,注意到:

\[|E'|=\frac{\sum_{u\in V'}d_u-cnt[V',\overline{V'}]}{2} \]

其中 \(d\) 为度数数组,\(cnt[V',\overline{V'}]\)\(V'\)\(\overline{V'}\) 之间边的数量。

代入 \(h(g)\) 得,

\[\begin{align*} h(g)&=\frac{1}{2}(\sum_{u\in V'}d_u-cnt[V',\overline{V'}]-2g|V'|)\\ &=\frac{1}{2}(\sum_{u\in V'}(d_u-2g)-cnt[V', \overline{V'}]) \end{align*} \]

此时与最小割建立联系,有:

\[-h(g)=\frac{1}{2}(\sum_{u\in V'}(2g-d_u)+cnt[V',\overline{V'}]) \]

由于 \(2g-d_u\) 可能为负数,设置偏移量 \(U=|E|\)

根据式子建图:

  • \(G[V']\) 内部边在网络图上连容量为 \(1\)双向边
  • \(\forall u\in V'\)\(s\)\(u\) 连容量为 \(2g-d_u+U\)单向边
  • \(\forall u\in V'\), \(u\)\(t\) 连容量为 \(U\)单向边

容易得到 \(h(g)=-\frac{maxflow(s,t)-|V|\cdot U}{2}\)

混合图欧拉回路

定义

给定图 \(G=(V,E)\),若 \(E\) 中即存在有向边,又存在无向边,则称图 \(G\)混合图

有向图欧拉回路存在性

\(\forall u\in V\),有 \(in_u=out_u\),则有向图 \(G=(V,E)\) 存在欧拉回路。其中 \(in,out\) 分别为入度数组和出度数组。

注意到这与网络流图的流量守恒是类似的。

算法

首先将无向边任意定向,此时每个点的出入度不一定都相等(即流量不守恒)。

引入源汇 \(s,t\) 调整度数差量,连边:

\[\begin{cases} s\rightarrow i:cap=\frac{out_i-in_i}{2} & out_i>in_i,\\ i\rightarrow t:cap=\frac{in_i-out_i}{2} & out_i<in_i. \end{cases} \]

原本定向为 \(<a,b>\) 的无向边连边为:\(a\rightarrow b: cap = 1\)

建图后从 \(s\)\(t\) 跑最大流。

算法思想与 \(\text{HLPP}\) 预流推进类似。

上下界网络流

无源汇上下界可行流

由于下界较难满足,先通过预推流满足下界,此时流量不一定守恒。

推流后剩下的残量加入网络流图中。

为满足流量守恒,同样引入源汇 \(s,t\) 调整盈余流量,设盈余流量 \(\delta_u=out_u-in_u\),连边:

\[\begin{cases} s\rightarrow i: cap = -\delta_i & \delta_i < 0,\\ i\rightarrow t: cap = \delta_i & \delta_i > 0. \end{cases} \]

可以发现,无源汇上下界可行流的算法本质上与混合图欧拉回路是一致的。

有源汇上下界可行流

从给定汇点向给定源点连容量为 \(\text{INF}\) 的边,即可将有源汇问题转换为无源汇问题。

有源汇上下界最大流

求解一遍可行流后,删除所有附加的点和边,在残余网络上从给定源点向给定汇点跑最大流扩流。

int main() {
	scanf("%d%d", &n, &m);
	memset(head, -1, sizeof head);
	int s = 0, t = n+1;
	for (int i = 1; i <= m; i++) {
		int u, v, l, r;
		scanf("%d%d%d%d", &u, &v, &l, &r);
		out[u] += l; in[v] += l;
		addedge(u, v, r-l);
	}
	for (int i = 1; i <= n; i++) {
		if (in[i] > out[i]) addedge(s, i, in[i]-out[i]);
		else addedge(i, t, out[i]-in[i]);
	}
	addedge(n, 1, INF);
	dinic(s, t);
	s = 1, t = n;
	int rst = e[cnt-1].cap;
	e[cnt-2].cap = e[cnt-1].cap = 0;
	rst += dinic(s, t);
	printf("%d", rst);
	return 0;
}

有源汇上下界最小流

预推流的下界流量不计入残量,因此在预推流后的网络上任意扩流推流均依然满足下界限制。

与最大流类似的,求解可行流后删除所有附加的点和边,在残余网络上从给定汇点向给定源点跑最大流退流。

int main() {
	scanf("%d%d", &n, &m);
	memset(head, -1, sizeof head);
	int s = 0, t = n+1;
	for (int i = 1; i <= m; i++) {
		int u, v, l, r;
		scanf("%d%d%d%d", &u, &v, &l, &r);
		out[u] += l; in[v] += l;
		addedge(u, v, r-l);
	}
	int sum = 0;
	for (int i = 1; i <= n; i++) {
		if (in[i] > out[i]) { addedge(s, i, in[i]-out[i]); sum += in[i]-out[i]; }
		else addedge(i, t, out[i]-in[i]);
	}
	addedge(n, 1, INF);
	dinic(s, t);
	s = n, t = 1;
	int rst = e[cnt-1].cap;
	e[cnt-2].cap = e[cnt-1].cap = 0;
	rst -= dinic(s, t);
	printf("%d", rst);
	
	return 0;
}
posted @ 2025-06-16 11:39  剑履山河  阅读(9)  评论(0)    收藏  举报