题目内容
给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点。
输入格式
输入第一行一个整数 n 表示区间的个数,接下来的 n 行,每一行两个用空格隔开的整数 a,b 表示区间的左右端点。1 <= n <= 50000, 0 <= ai <= bi <= 50000 并且 1 <= ci <= bi - ai+1。
输出格式
输出一个整数表示最少选取的点的个数。
输入样例
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1
输出样例
6
备注
本题的局部贪心解法请移步:https://blog.csdn.net/qq_44506233/article/details/104702237
解题思路
使用差分约束的角度来分析这道题。
关于差分约束的讲述,请在本站中自行搜索“差分约束”。
题目要求:
- 在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点
即,如果我们设置数组sum,其中sum[i]意为从零点到点i这个区间中所有的点的数目。那么对于上述的要求,我们可以得到:
- sum[bi]-sum[ai-1] >= ci
除此之外,题目中还有一个隐藏条件:
- 对于某个位置,只有两种情况:
- 1.这个位置上没有点
- 2.这个位置上有点
那么如何转换成不等式的形式呢?
- sum[i]-sum[i-1] >= 0
- sum[i]-sum[i-1] <= 1
而由于题目中要求的是整个区间范围内所取的点的最少数目,即当整个区间的最右端点为b时,需要求:
- sum[b]>=T
这里这个T就是我们所要求的答案。
把所有的不等式转换成>=形式跑最长路即可。
Tips:最后的T的那个不等式不符合我们解差分约束的不等式的格式,所以我们可以人为把输入的所有的点都+1,然后把sum[0]设置为0即可,此时的答案就是:
- sum[b+1]-sum[0] >=T
下面给出代码:
#include <iostream>
#include <cstring>
#include <queue>
const int INF = 0x3f3f3f3f;
const int maxn = 500005;
using namespace std;
int n, mostright, cur;
int head[maxn], visit[maxn], sum[maxn];
struct Node
{
int v, w, next;
}edge[maxn];
void addEdge(int u, int v, int w)
{
edge[cur].v = v;
edge[cur].w = w;
edge[cur].next = head[u];
head[u] = cur;
cur++;
}
void SPFA()
{
queue<int>q;
q.push(0);
sum[0] = 0;
visit[0] = 1;
while (!q.empty())
{
int now = q.front();
q.pop();
visit[now] = 0;
for (int i = head[now]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
int w = edge[i].w;
if (sum[v] < sum[now] + w)
{
sum[v] = sum[now] + w;
if (!visit[v])
{
q.push(v);
visit[v] = 1;
}
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
memset(visit, 0, sizeof visit);
memset(head, -1, sizeof head);
mostright = 0;
cur = 0;
cin >> n;
for (int i = 0, a, b, c; i < n; i++)
{
cin >> a >> b >> c;
addEdge(a, b + 1, c);
if (b + 1 > mostright)
{
mostright = b + 1;
}
}
for (int i = 0; i <= mostright; i++)
{
sum[i] = -INF;
addEdge(i, i + 1, 0);
addEdge(i + 1, i, -1);
}
SPFA();
cout << sum[mostright] << endl;
}