【ACWing】362. 区间

该博客介绍了一个利用SPFA算法解决区间覆盖问题的实例。给定一系列区间和每个区间的覆盖需求,目标是找到最小的整数集合,确保每个区间内至少有指定数量的整数被覆盖。通过将问题转化为差分约束问题并建立图,使用SPFA算法求解最长路,从而找出满足条件的最小集合大小。代码示例展示了如何实现这一过程。
摘要由CSDN通过智能技术生成

题目地址:

https://www.acwing.com/problem/content/364/

给定 n n n个区间 [ a i , b i ] [a_i,b_i] [ai,bi] n n n个整数 c i c_i ci。你需要构造一个整数集合 Z Z Z,使得 ∀ i ∈ [ 1 , n ] ∀i∈[1,n] i[1,n] Z Z Z中满足 a i ≤ x ≤ b i a_i≤x≤b_i aixbi的整数 x x x不少于 c i c_i ci个。求这样的整数集合 Z Z Z最少包含多少个数。

输入格式:
第一行包含整数 n n n。接下来 n n n行,每行包含三个整数 a i , b i , c i a_i,b_i,c_i ai,bi,ci

输出格式:
输出一个整数表示结果。

数据范围:
1 ≤ n ≤ 50000 1≤n≤50000 1n50000
0 ≤ a i , b i ≤ 50000 0≤a_i,b_i≤50000 0ai,bi50000
1 ≤ c i ≤ b i − a i + 1 1≤c_i≤b_i-a_i+1 1cibiai+1

s [ i ] s[i] s[i]是在 1 ∼ 50001 1\sim 50001 150001中,前 i i i个数中选了多少个数。为了方便我们将所有区间向右平移 1 1 1个单位,使得所有数都位于 [ 1 , 50001 ] [1,50001] [1,50001]里。那么 s s s需要满足以下条件:
1、 s [ 0 ] = 0 s[0]=0 s[0]=0
2、 s [ i ] ≥ s [ i − 1 ] , s [ i ] − s [ i − 1 ] ≤ 1 s[i]\ge s[i-1],s[i]-s[i-1]\le 1 s[i]s[i1],s[i]s[i1]1,意思是 i i i这个数最多选 1 1 1次(因为题目里的 Z Z Z是个整数集合,集合是不能有重复元素的);
3、 s [ b i ] − s [ a i − 1 ] ≥ c i s[b_i]-s[a_i-1]\ge c_i s[bi]s[ai1]ci,表示 [ a i , b i ] [a_i,b_i] [ai,bi]这个区间里取的数至少有 c i c_i ci个。
这样就将原问题转化为了一个差分约束问题,要求的就是 s [ 50001 ] s[50001] s[50001]的最小值,所以需要将每个不等式变为 s [ x ] ≥ s [ y ] + z s[x]\ge s[y]+z s[x]s[y]+z的形式,然后建图求最长路(从 y y y x x x连一条长 z z z的边即可),可以用SPFA来做。代码如下:

#include <iostream>
#include <cstring>
using namespace std;

const int N = 50010, M = 3 * N;
int n;
int h[N], e[M], ne[M], w[M], idx;
int dist[N];
int q[N];
bool st[N];

void add(int a, int b, int c) {
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

void spfa() {
    memset(dist, -0x3f, sizeof dist);
    dist[0] = 0;
    int hh = 0, tt = 0;
    q[tt++] = 0;
    st[0] = true;

	// 循环队列里要写 while (hh != tt)
    while (hh != tt) {
        int t = q[hh++];
        if (hh == N) hh = 0;
        st[t] = false;

        for (int i = h[t]; ~i; i = ne[i]) {
            int v = e[i];
            if (dist[v] < dist[t] + w[i]) {
                dist[v] = dist[t] + w[i];
                if (!st[v]) {
                    q[tt++] = v;
                    if (tt == N) tt = 0;
                    st[v] = true;
                }
            }
        }
    }
    
}

int main() {
    scanf("%d", &n);
    memset(h, -1, sizeof h);
    for (int i = 1; i <= 50001; i++) {
        add(i - 1, i, 0);
        add(i, i - 1, -1);
    }

    while (n--) {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        // 注意将所有区间向右平移一个单位
        a++, b++;
        add(a - 1, b, c);
    }
    
    spfa();

    printf("%d\n", dist[50001]);

    return 0;
}

时间复杂度 O ( n m ) O(nm) O(nm),空间 O ( n ) O(n) O(n) n n n m m m分别是图的点数和边数。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值