P3357 最长k可重线段集问题(限制建图+拆点+最大费用最大流)

传送门

题目描述:

给定实直线 L 上n 个开区间组成的集合 I,和一个正整数 k,试设计一个算法,从开区间集合 I 中选取出开区间集合 S,SI,使得在实直线 L 上的任意一点 x

S 中包含 x 的开区间个数不超过 k,且zSz∣ 达到最大(z∣ 表示开区间 z的长度)。

这样的集合S 称为开区间集合I 的最长 k 可重区间集。zSz∣ 称为最长 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;
}

 

posted @ 2021-04-16 16:43  cono奇犽哒  阅读(56)  评论(0)    收藏  举报