差分约束:区间选点。c++

区间选点
给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点,要求使用差分约束

输入第一行一个整数 n 表示区间的个数,接下来的 n 行,每一行两个用空格隔开的整数 a,b 表示区间的左右端点。1 <= n <= 50000, 0 <= ai <= bi <= 50000 并且 1 <= ci <= bi - ai+1。

输出一个整数表示最少选取的点的个数

sample input:
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1

sample output:
6

思路:

  • 因为题目要求使用差分约束,所以需要找到它的约束条件
  • 差分约束的每个约束条件是由两个其中的变量做差构成的,比如xi-xj<ck(ck为常数,可正可负)
  • 差分约束就是列出所有的约束条件,然后求一组解x1,x2,x3…使得所有的约束条件得到满足,否则判断出无解
  • 对于差分约束系统,都可以转换为图论中单源最短路问题,差分约束的每一个不等式约束条件xi-xj<ck都可以通过移相转变为xi<ck+xj,这样就和图论中的dis[i] < dis[j] +w(u,v)可以对应上,对于差分约束的求解其实就可以对应最短路问题中的松弛操作
  • 因为我们要保证解的存在性,所以使用SPFA算法来判断负环是否存在是有必要的,具体思路可以参考之前SPFA的例题
  • 对于本题,由于需要第i个区间中里至少有ci个点,所以我们假设 i 到数轴起点1放置的点数为dis[i] ,存在dis[bi] - dis[ai-1] >=ci (条件一)
  • 后面的点的dis值肯定大于前面的点的dis : dis[a]>=dis[a-1] (条件二)
  • 同时需要注意给定的条件,为了使得dis有意义,0<=dis[i+1] - dis[i]<=1 (条件三)
  • 最后我们把0视为源点,初始化每个点与后一点之间的边的距离关系,利用spfa来求最长路来求出最长路径的权值和的最小解,最终输出所有区间最右端点max的路径长即可
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<cmath>
using namespace std;
const int N=5e+5;
int maxx;
struct Edge
{
 int to,next,w;
}e[N];
int head[N],dis[N],tot;
bool visit[N];
queue<int> q;
void add(int u,int v,int w)
{ 
 e[tot].to=v;
 e[tot].next=head[u];
 e[tot].w=w;
 head[u]=tot;
 tot++;
}
void spfa(int a) 
{
 for(int i = 0; i <= maxx;i++) 
  dis[i] = -N;
 memset(visit,false,sizeof(visit));
 q.push(a); 
 dis[a] = 0; 
 visit[a] = true;  
 while(!q.empty())
 {
  int temp = q.front(); 
  q.pop(); 
  visit[temp] = false; 
   for(int i = head[temp]; i!=-1; i = e[i].next) 
   {
   int j = e[i].to;  
   if(dis[j] < dis[temp] + e[i].w) 
   {
    dis[j] = dis[temp] + e[i].w;
    if(!visit[j])
    {
     q.push(j);
     visit[j] = true;
    }
   }
   } 
 } 
}
int main()
{
 int n;
    scanf("%d",&n);
    tot=0;
 maxx=-1;
 memset(head,-1,sizeof(head));
    while(n--)
 {
  int u,v,w;
     scanf("%d%d%d",&u,&v,&w); 
  maxx=max(v,maxx);
     add(u,v+1,w); 
    }  
    maxx++;
    for(int i=0;i<=maxx;++i) 
 {
     add(i,i+1,0);
     add(i+1,i,-1);
 }
    spfa(0);
 cout<<dis[maxx];
 return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值