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
xi−xj≤ck都可以移项变形为
x
i
≤
c
k
+
x
j
x_i \le c_k+x_j
xi≤ck+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[ai−1]≥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
0≤sum[i]−sum[i−1]≥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;
}