差分约束-区间选点问题

题意:
给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点。
输入:
输入第一行一个整数 n 表示区间的个数,接下来的 n 行,每一行两个用空格隔开的整数 a,b 表示区间的左右端点。1 <= n <= 50000, 0 <= ai <= bi <= 50000 并且 1 <= ci <= bi - ai+1。
输出:
输出一个整数表示最少选取的点的个数
输入样例:
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1
输出样例:
6
解题思路:
之前学过用贪心的思路去完成,这里考察对差分约束的掌握。差分约束就必须要构造不等式组,我们用𝑠𝑢𝑚[𝑖] 表示数轴上 [0, 𝑖] 之间的选点的个数,对于第i个区间[𝑎i,𝑏i]需要满足𝑠𝑢𝑚 [𝑏i] −𝑠𝑢𝑚[𝑎i−1] ≥𝑐i,同时还需要保证sum 是有意义的,即 :0≤𝑠𝑢𝑚[ 𝑖 ] −𝑠𝑢𝑚[ 𝑖−1 ] ≤1,题目要求该差分约束系统的最小解,转化为 ≥ 不等式组用spfa跑最长路,答案为𝑠𝑢𝑚[max{𝑏i}]。
注意事项:
注意到不等式0≤𝑠𝑢𝑚[0]−𝑠𝑢𝑚[-1] ≤1,而数组下标是从0开始的,故我们对题目中的每一点的编号都加1,因此点的编号是从0开始的由于sum[-1]=0,故点的编号变换以后sum[0]=0,且0用来作为源点s这个初始条件。
总结:
一道已经做过的题目,之前的方法是贪心算法,这次是差分约束,差分约束的关键是找到想要的不等式组的的形式,然后转化为最短路最长路问题用spfa来解决,在最长路问题中初始化dis数组为-inf且松弛条件的符号与最短路问题中相反。
参考代码:

#include <iostream>
#include <queue>
#include <limits.h>
#include <cstring>
#include <cmath>
using namespace std;
int n;
int num=0;
int dis[50010],inq[50010];
const int INF=INT_MAX;
struct edge{
    int to, next ,w;
    edge(int a=0,int b=-1,int c=0):to(a),next(b),w(c){}
} e[200000];
int head[50010],tot=0;
void add(int x,int y,int w){
    e[tot].to = y;
    e[tot].next = head[x];
    e[tot].w = w;
    head[x] = tot;
    tot++;
}

int main(int argc, const char * argv[]) {
        cin>>n;
    int max=0;
    memset(head, -1, sizeof(head));
    for (int i=1; i<=n; i++) {
        int a,b,c;cin>>a>>b>>c;
        add(a, b+1, c);
        if(b+1>max)max=b+1;
    }
    for (int i=1; i<=max; i++) {
        add(i-1, i, 0);
        add(i, i-1, -1);
    }
    
    
        for(int i=0;i<=max;++i){
        dis[i] = -INF;
        inq[i] = 0;
        }
        int s=0;
        dis[s] = 0;
        inq[s] = 1;
        queue<int> q;
        q.push(s);
        while(!q.empty()) {
        int u = q.front(); q.pop();inq[u] = 0;
        for(int i=head[u];i!=-1;i=e[i].next) {
        int v = e[i].to;
            if(dis[v] < dis[u] + e[i].w) {
                dis[v] = dis[u] + e[i].w;
                if(!inq[v]) {
                     q.push(v);
                    inq[v] = 1;
                        }
                     }
        }
        }
    cout<<dis[max]<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值