题目原文
给定一个数轴上的 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
解题思路
由题知到bi的所有点数d[bi]减去到ai的所有点数d[ai]应大于等于ci,即d[bi]-d[ai-1]>=ci;
同时要使d[]有意义,需要0=<d[i]-d[i-1]<=1.
构造大于等于差分约束得:
d[bi]>=d[ai-1]+ci
d[i]>=d[i-1]
d[i-1]>=d[i]+(-1)
根据差分约束式建边,利用SPFA(判断条件改为if(!(d[x]>=d[y]+w)))求解即可。
实现代码
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 50010;
const int maxm = 150010;
struct Edge {
int x, y, w, next;
Edge() {
x = y = w = next = 0;
}
Edge(int x, int y, int w, int cnt) {
this->x = x;
this->y = y;
this->w = w;
this->next = cnt;
}
};
Edge e[maxm];
int h[maxn];
int d[maxn];
bool in[maxn];
//插入单项边
void makeE(int x, int y, int w, int cnt) {
e[cnt] = Edge(x, y, w, h[x]);
h[x] = cnt;
}
void search(int st) {//SPFA
memset(in, 0, sizeof(in));
queue<int>q;
//push三件套
d[st] = 0;
q.push(st);
in[st] = 1;
while (!q.empty()) {
int x = q.front();
q.pop();
in[x] = 0;
for (int i = h[x]; i; i = e[i].next)
{
int y = e[i].y, w = e[i].w;
if (!(d[y] >=w + d[x]))
{
d[y] = w + d[x];
if (!in[y])
{
q.push(y);
in[y] = 1;
}
}
}
}
}
int main() {
int n;
cin >> n;
int cnt = 0;
int x, y, w, next, end = 0;
for (int i = 1; i <= n; i++) {
scanf("%d %d %d", &x, &y, &w);
makeE(x, y+1, w, ++cnt);
//st = min(st, x);
end = max(end, y+1);
}
//初始化d
for (int i = 0; i <=end; i++)
{
d[i] = -1e10;
}
for(int i=1;i<=end;i++)
{
makeE(i-1,i,0,++cnt);
makeE(i,i-1,-1,++cnt);
}
search(0);//0,注意从0开始
cout << d[end] << endl;
//system("pause");
return 0;
}
小结
在建边时需要注意先将端点bi进行平移(bi+1),因为需要计算bi-(ai-1),而题中所给范围为0 <= ai <= bi <= 50000,包括0端点。