问题描述
给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点。
Input
输入第一行一个整数 n 表示区间的个数,接下来的 n 行,每一行两个用空格隔开的整数 a,b 表示区间的左右端点。1 <= n <= 50000, 0 <= ai <= bi <= 50000 并且 1 <= ci <= bi - ai+1。
Output
输出一个整数表示最少选取的点的个数
Sample Input
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1
Sample Output
6
解题思路以及关键代码
使用差分约束系统的思路
关于差分约束的几个解释
①
②关于解的存在性
1.存在负环(正环) 分别对应跑最短路和最长路
不等式约束上表现为xi-x1<=T T为无穷小(举个例子,x1-1<=x2 , x2-1<=x1越跑越小)
2.终点不可达
表明xi与x1之间没有约束条件,对应代码中dis[i]=inf.
③关于跑最长路还是最短路???
1.如果差分约束系统是<=则跑最短路
2.相反差分约束系统是>=则跑最长路
这是因为要满足多个不等式约束,所以跑最短路得到的是上界(最大解)。
跑最长路得到的是下界(最小解)。
对于本题
构造差分约束系统不等式如下:(求最小值跑最长路)
因为本题数据保证一定有解,所以不需要判断正环。
如果要判断正环的话,要注意 本题给的n是区间的个数,而不是等价为图之后点的个数。
点的个数为themax - themin + 1
全部代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<map>
#include<cmath>
using namespace std;
const int maxn = 50005;
const int maxm = 500005;
const int inf = 0x3fffffff;
int themin=inf,themax=-inf;
int n,m,a[maxn];
//链式前向星
struct Edge
{
int to,next,w;
} edge[maxm];
int head[maxn],cnt;
void init()
{
cnt=0;
memset(head,-1,sizeof(head));
}
void add(int u,int v,int w)
{
edge[cnt].to=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt;
cnt++;
}
//spfa
int vis[maxn],cn[maxn],dis[maxn];
//vis - 点在不在队列中 cn【x】 - 点x最短路径上经过的点数 dis - 距离
queue<int> q;
void spfa(int s)//本题一定有解 所以不用判定有没正负环
{
//初始化
while(q.size()) q.pop();
for(int i=0;i<=n;i++)
{
vis[i] = 0;
dis[i] = -inf;
}
//队列中加入初始点
dis[s] = 0; vis[s] = 1;
q.push(s);
while(q.size())
{
int now = q.front(); q.pop();
vis[now] = 0;//每一个点可以被队列弹出多次
//松弛操作
for(int i=head[now];~i;i=edge[i].next)
{
int u = now, v = edge[i].to,w = edge[i].w;
if(dis[v] < dis[u] + w)
{
dis[v] = dis[u] + w;
if(!vis[v])
{
vis[v] = 1;
q.push(v);
}
}
}
}
}
int main()
{
init();
scanf("%d",&n);
int u,v,w;
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&u,&v,&w);
u+=1,v+=1; //相当于平移操作 因为防止数组左越界
add(u-1,v,w);
themax=max(v,themax);
themin=min(u-1,themin);
}
for(int i=themin;i<=themax;i++)
{
add(i-1,i,0);
add(i,i-1,-1);
}
spfa(themin);
printf("%d",dis[themax]);
return 0;
}