[Luogu P3358] [网络流24题] 最长k可重区间集问题

洛谷传送门

题目描述

img

对于给定的开区间集合 I I I 和正整数 k k k,计算开区间集合 I I I 的最长 k k k可重区间集的长度。

输入输出格式

输入格式:

输入的第 1 1 1 行有 2 2 2 个正整数 n n n k k k,分别表示开区间的个数和开区间的可重叠数。接下来的 n n n行,每行有 2 2 2 个整数,表示开区间的左右端点坐标。

输出格式:

将计算出的最长 k k k可重区间集的长度输出

输入输出样例

输入样例#1:
4 2
1 7
6 8
7 10
9 13 
输出样例#1:
15

说明

对于100%的数据, 1 ≤ n ≤ 500 1\le n\le 500 1n500, 1 ≤ k ≤ 3 1\le k\le 3 1k3

解题分析

费用流经典模型, 还适用于一些需要线性规划转费用流的题目?

考虑将所有端点从小到大连流量为限制的边, 表示可以流过的流量。

l e f i lef_i lefi r i g i rig_i rigi连流量为 1 1 1, 花费为 r i g i − l e f i rig_i-lef_i rigilefi的边, 表示可以选这条边, 同时区间中可用流量 − 1 -1 1

最后跑一遍最大费用流即可。

端点可能很大, 需要离散化。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <queue>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define INF 1e8
#define MX 1050
bool neg;
template <class C>
IN void in(C &x)
{
    x = 0; R char c = gc;
    for (; !isdigit(c); c = gc)
    if (c == '-') neg = true;
    for (;  isdigit(c); c = gc)
    x = (x << 1) + (x << 3) + c - 48;
    if (neg) neg = false, x = -x;
}
template <class C> IN C min(C a, C b) {return a < b ? a : b;}
int n, tot, dif, S, T, ans, k, cnt;
int head[MX], dis[MX], del[MX], pre[MX], from[MX], to[MX], buc[MX];
bool inq[MX];
struct Edge {int to, fl, len, nex;} edge[MX * 10];
IN void add(R int from, R int to, R int fl, R int len)
{
    edge[++cnt] = {to, fl, len, head[from]}, head[from] = cnt;
    edge[++cnt] = {from, 0, -len, head[to]}, head[to] = cnt;
}
namespace MCMF
{
    std::queue <int> q;
    IN bool SPFA()
    {
        std::memset(dis, 63, sizeof(dis));
        dis[S] = 0, del[S] = INF, q.push(S);
        R int now;
        W (!q.empty())
        {
            now = q.front(); q.pop();
            for (R int i = head[now]; ~i; i = edge[i].nex)
            {
                if (dis[edge[i].to] > dis[now] + edge[i].len && edge[i].fl)
                {
                    dis[edge[i].to] = dis[now] + edge[i].len;
                    pre[edge[i].to] = i;
                    del[edge[i].to] = min(del[now], edge[i].fl);
                    if (!inq[edge[i].to]) inq[edge[i].to] = true, q.push(edge[i].to);
                }
            }
            inq[now] = false;
        }
        return dis[T] < INF;
    }
    void update()
    {
        R int now = T, pr;
        W (now ^ S)
        {
            pr = pre[now];
            edge[pr].fl -= del[T], edge[pr ^ 1].fl += del[T];
            now = edge[pr ^ 1].to;
        }
        ans -= del[T] * dis[T];
    }
    void init()
    {
        W (SPFA()) update();
        printf("%d", ans);
    }
}
int main(void)
{
    in(n), in(k);
    std::memset(head, cnt = -1, sizeof(head));
    for (R int i = 1; i <= n; ++i)
    {
        in(from[i]), in(to[i]);
        buc[++tot] = from[i], buc[++tot] = to[i];
    }
    std::sort(buc + 1, buc + 1 + tot);
    dif = std::unique(buc + 1, buc + 1 + tot) - buc - 1;
    T = dif + 1;
    for (R int i = 1; i <= dif; ++i) add(i - 1, i, k, 0);
    add(dif, T, k, 0);
    for (R int i = 1; i <= n; ++i) add(std::lower_bound(buc + 1, buc + 1 + dif, from[i]) - buc, std::lower_bound(buc + 1, buc + 1 + dif, to[i]) - buc, 1, -to[i] + from[i]);
    MCMF::init();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值