P3358 最长k可重区间集问题

题目链接 (Click) (Here)

这题的写法非常巧妙。

每个位置的点向它的下一个位置连一个容量为(INF)的边,从区间的左端点往右端点拉一条容量为(1),费用为区间长度的边,从起点向点(1)连一条容量为(k)的边,限制只能流(k)次。这个建模的巧妙之处就在,它并没有明面上限制某个点最多经过(k)次,却实际上使路径单向,从而每一次流过都最多经过每个点一次,同时也并不会对并没有相交的区间造成影响。

#include <bits/stdc++.h>
using namespace std;

const int N = 100010;
const int M = 400010;
const int INF = 0x3f3f3f3f;

int n, k, tot, l[N], r[N], len[N], pos[N];

int cnt = -1, head[N];

struct edge {
	int nxt, to, w, f;
}e[M];

void add_edge (int from, int to, int flw, int val) {
	e[++cnt].nxt = head[from];
	e[cnt].to = to;
	e[cnt].w = val;
	e[cnt].f = flw;
	head[from] = cnt;
}

void add_len (int u, int v, int f, int w) {
	add_edge (u, v, f, +w);
	add_edge (v, u, 0, -w);
}

void discretize () {
	for (int i = 1; i <= n; ++i) {
		cin >> l[i] >> r[i];
		len[i] = r[i] - l[i];
		pos[i * 2 - 1] = l[i], pos[i * 2] = r[i];
	}
	sort (pos + 1, pos + 1 + n * 2);
	tot = unique (pos + 1, pos + 1 + n * 2) - pos - 1;
   	for (int i = 1; i <= n; ++i) {
		l[i] = lower_bound (pos + 1, pos + 1 + tot, l[i]) - pos;
		r[i] = lower_bound (pos + 1, pos + 1 + tot, r[i]) - pos;
	}
}

queue <int> q;
int dis[N], vis[N], flow[N];
int pre_node[N], pre_edge[N];

int spfa (int s, int t) {
	memset (vis, 0, sizeof (vis));
	memset (dis, -0x3f, sizeof (dis));
	memset (flow, 0x3f, sizeof (flow));
	q.push (s); vis[s] = true; dis[s] = 0;
	while (!q.empty ()) {
		int u = q.front (); q.pop ();
		for (int i = head[u]; ~i; i = e[i].nxt) {
			int v = e[i].to;
			if (dis[v] < dis[u] + e[i].w && e[i].f) {
				dis[v] = dis[u] + e[i].w;
				flow[v] = min (flow[u], e[i].f);
				pre_edge[v] = i;
				pre_node[v] = u;
				if (!vis[v]) {
					vis[v] = true;
					q.push (v);
				}
			}
		}
		vis[u] = false;
	}
	return dis[t] != dis[0];
}

int main () {
	memset (head, -1, sizeof (head));
	cin >> n >> k;
	discretize ();
	int s = tot + 1, t = tot + 2;
	add_len (s, 1, k, 0);
	add_len (tot, t, k, 0);
	for (int i = 1; i < tot; ++i) {
		add_len (i, i + 1, INF, 0);
	}
	for (int i = 1; i <= n; ++i) {
		add_len (l[i], r[i], 1, len[i]);
	}
	int max_cost = 0;
	while (spfa (s, t)) {
		max_cost += dis[t] * flow[t];
		int u = t;
		while (u != s) {
			e[pre_edge[u] ^ 0].f -= flow[t];
			e[pre_edge[u] ^ 1].f += flow[t];
			u = pre_node[u];
		}
	}
	cout << max_cost << endl;
} 

原文地址:https://www.cnblogs.com/maomao9173/p/10478144.html