WEEK8 周记 作业——差分约束_区间选点

WEEK8 周记 作业——差分约束_区间选点

一、题意

1.简述

给定一个数轴上的 n n n 个区间,要求在数轴上选取最少的点使得第 i i i 个区间 [ a i , b i ] [ai, bi] [ai,bi] 里至少有 c i c_i ci 个点。

2.输入格式

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

3.输出格式

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

4.样例

Input

5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1

Output

6

二、算法

主要思路

求解差分约束系统,可以转化为图论中的单源最短路问题。
对于差分约束中的每一个不等式 x i − x j ≤ c k x_i-x_j \le c_k xixjck都可以移项变形为 x i ≤ c k + x j x_i \le c_k+x_j xick+xj
在这里插入图片描述
d i s [ v ] > d i s [ u ] + w ( u , v ) dis[v] > dis[u] + w(u,v) dis[v]>dis[u]+w(u,v) 是松弛条件,因为 d i s [ v ] dis[v] dis[v] 不满足 d i s [ i ] < = d i s [ j ] + w ( i , j ) dis[i] <= dis[j] + w(i,j) dis[i]<=dis[j]+w(i,j) 这个式子,所以才需要松弛。
需要注意的是,求最大解的时候用最短路,求最小解时用最长路。
该题求的是最小解,所以用最长路SPFA(为了应对负边的情况)
s u m [ i ] sum[i] sum[i]表示数轴上 [ 0 , i ] [0,i] [0,i]之间选点的个数
对于第i个区间 [ a i , b i ] [a_i,b_i] [ai,bi]需要满足 s u m [ b i ] − s u m [ a i − 1 ] ≥ c i sum[b_i]-sum[a_i-1]\ge c_i sum[bi]sum[ai1]ci
除此之外我们还需要保证 s u m sum sum是有意义的: 0 ≤ s u m [ i ] − s u m [ i − 1 ] ≥ 1 0\le sum[i]-sum[i-1]\ge 1 0sum[i]sum[i1]1
求该差分约束系统的最小解,最终转化为 ≥ \ge 不等式组跑最长路,答案为 s u m [ m a x { b i } ] sum[max\{ b_i\}] sum[max{bi}]


三、代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
using namespace std;
struct edge{
	int next;
	int to;
	int dis; 
} ed[200010];//从1开始 //这里边的条数要大于50000,因为除了输入之外还有其他的约束条件 
int num_edge;
int head[50010];//记录当前以from为起点的所有边中的最末尾边的编号(在edge数组中索引)
int inq[50010];
//int dis[50010];
int sum[50010];//sum[0]相当于区间【-1,0】中点的数目所以必为0 
queue<int> q;
int maxb = 0;
const int inf = 1e8;
void add(int from,int to,int dis){
	ed[++num_edge].dis = dis;
	ed[num_edge].next = head[from];
	ed[num_edge].to = to;
	head[from] = num_edge; 
}
void SPFA(){
	for(int i=0;i<=maxb;i++) sum[i] = -inf;//必须得小于0,-1都行(具体数值与sum[0]的选取有一定关联,否则启动不了下面的算法 
	sum[0] = 0;
	inq[0] = 1;
	q.push(0);
	while(!q.empty()){
		int from = q.front();
		q.pop();
		inq[from] = 0;
		for(int next=head[from];next!=0;next = ed[next].next){
			if(sum[ed[next].to]<ed[next].dis+sum[from]){
				sum[ed[next].to] = ed[next].dis+sum[from];
				if(inq[ed[next].to]==0){//注意这里压队的应该是ed[next].to 
					q.push(ed[next].to);
					inq[ed[next].to] = 1;
				}
			}
		}
	}
}
int main(){
	int n,i;
	scanf("%d",&n);
	for(i=0;i<n;i++){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		add(a,b+1,c);
		maxb = b+1>maxb?b+1:maxb;
	}
	for(i=1;i<=maxb;i++){
		add(i-1,i,0);
		add(i,i-1,-1);//这两处i和i-1一定不能反了!谁在前谁在后要想清楚 
	}
	SPFA();
	printf("%d",sum[maxb]);
	return 0;
}           
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值