P3357 最长k可重线段集问题(限制建图+拆点+最大费用最大流)
题目描述:
给定实直线 L 上n 个开区间组成的集合 I,和一个正整数 k,试设计一个算法,从开区间集合 I 中选取出开区间集合 S,S⊆I,使得在实直线 L 上的任意一点 x,
S 中包含 x 的开区间个数不超过 k,且∑z∈S∣z∣ 达到最大(∣z∣ 表示开区间 z的长度)。
这样的集合S 称为开区间集合I 的最长 k 可重区间集。∑z∈S∣z∣ 称为最长 k 可重区间集的长度。
对于给定的开区间集合I 和正整数 k,计算开区间集合 I 的最长k 可重区间集的长度。
思路:给我们一些区间,我们要选择一些区间,使得选择出的区间的长度和最大,限制条件是,选出的区间重叠部分不能超过k层。
我们可以建立一个超级源点s,和一个源点ss,让超级源点s对ss连容量为k,费用为0的边,表示最多重叠次数,
由于每个区间的长度只能计算一次,我们对每个区间拆点,连容量为1,费用为该区间长度,然后用源点ss对区间
连容量为1,费用为0的边,表示可以选择该区间,对区间排序,然后按照从左向右把那些能共存不重叠的区间连边,
可以参考航空路线问题的连边方式,然后区间的发送点对汇点连容量为1,费用为0的边,跑最大费用最大流,就能得到
最大费用就是结果。
参考博客:传送门
建图代码:
struct po { int x1, x2,len; bool operator<(po b) { return x1 < b.x1; } }a[maxn]; int main() { //freopen("test.txt", "r", stdin); scanf("%d%d", &n, &k); for (int i = 1; i <= n; i++) { scanf("%d%d", &a[i].x1, &a[i].x2); a[i].len = a[i].x2 - a[i].x1; } sort(a + 1, a + n + 1); s = 0, t = n * 2 + 2; int ss = n * 2 + 1; add(s, ss, k, 0); for (int i = 1; i <= n; i++) { add(ss, i, 1, 0); add(i, i + n, 1, a[i].len); add(i + n, t, 1, 0); } for (int i = 1; i <= n; i++) { for (int j = i + 1; j <= n; j++) { if (a[i].x2 <= a[j].x1) { add(i + n, j, 1, 0); } } } mcmf(); printf("%lld\n", mincost); return 0; }

浙公网安备 33010602011771号