题意:
给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点。
input:
输入第一行一个整数 n 表示区间的个数,接下来的 n 行,每一行两个用空格隔开的整数 a,b 表示区间的左右端点。1 <= n <= 50000, 0 <= ai <= bi <= 50000 并且 1 <= ci <= bi - ai+1。
output:
输出一个整数表示最少选取的点的个数。
样例数据:
input:
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1
output:
6
思路:
(小声说,如果是自己写题,肯定是无论如何也想不到这么高端的差分约束的做法。)用差分约束,主要就是思考如何构建差分约束的条件,即如何构建差分约束的图。将图构建完成后,利用spfa来根据题意,是跑最常路还是最短路,修改条件即可。
构建图:首先要构造不等式组。可以记sum[i](在此处用的dis),为数轴【0,i】之间需要选点的个数。
**对于第i个区间[ai,bi]之间需要满足,sum[bi]-sum[ai-1]>=ci,其中ci即为这个区间需要选点的个数,另外为了保证sum有意义,需要保证 0<=sum[i]-sum[i-1]<=1。**根据上述的条件即可构建图来。
如何构建图:
在此附上学长给的链接,跟着说明来构建图还是能理解的。
https://blog.csdn.net/kk303/article/details/6864199
即按照上述来选择起始点和终点以及边权,构建好图,在利用spfa即可得到所需要的解。注意跑最短路得到的是最大解,跑最长路得到的是最小的解。还要注意在这样构建时,由于左区间,即起始点有a-1的限制,故原先的起点0会多出来一个-1点,即增加一个点即可(如果这个点没有路,多出来的-1也并不影响,因为其到0的距离为0)。
代码:
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <string.h>
#include <queue>
#include <cmath>
using namespace std;
int n;
int a, b,c;
int dis[50010]; //记录长度
int vis[50010]; //是否在队列中
int cnt[50010]; //记录到达该点所经历的边数
struct Edge{
int to, next, w; //到哪点 下一条边 边的权值
}e[200050];
int head[50010];
int tot; //当前的边数
queue<int> q;
// 到哪点 去哪点 权值
void add_edge(int t, int f, int w){
e[tot].to=t;
e[tot].next=head[f];
head[f]=tot;
e[tot].w=w;
tot++;
}
queue <int> q2;
bool vis2[205];
void bfs(int x){
while (q2.size()) q2.pop();
memset(vis2,0,sizeof(vis2));
dis[x]=-1;
vis2[x] = 1;
q2.push(x);
while(q2.size()){
int x = q2.front();
q2.pop();
int i = head[x];
while (i != -1) {
int t1 = e[i].to;
if(vis2[t1]==0){
q2.push(t1);
vis2[t1]=1;
dis[t1]=-1;
}
i = e[i].next;
}
}
}
void spfa() {
while (q.size()) q.pop();
//初始化
dis[0] = 0, vis[0]=1;
q.push(0);
while (q.size()) {
int x = q.front();
q.pop();
vis[x] = 0; //x出队列,标记值为0,在spfa中可以被队列弹出多次
int i = head[x];
while (i != -1) {
int t1 = e[i].to;
int w1 = e[i].w;
if (dis[t1] < dis[x] + w1) {
dis[t1] = dis[x] + w1; //记录路径长度
cnt[t1]=cnt[x]+1;
if(cnt[t1]>=50000){
bfs(t1);
}else if(dis[t1]!=-1 && vis[t1]==0){
q.push(t1);
vis[t1]=1;
}
}
i = e[i].next;
}
}
}
int main(){
scanf("%d",&n);
tot=0;
//初始化 0-50000 由于 a-1,故多出一个点
for(int i=0; i<=50001; i++){
head[i]=-1;
dis[i]=-10000;
vis[i]=0;
cnt[i]=0;
}
for(int i=0; i<50001; i++){
add_edge(i+1,i,0);
add_edge(i,i+1,-1);
}
for(int i=0; i<n; i++){
scanf("%d%d%d",&a,&b,&c);
add_edge(b+1,a,c); //多了个-1点 ,故区间总体右移
}
spfa();
cout<<dis[50001]<<endl;
}