题目
给定一个数轴上的 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
思路
一、差分约束
学长的ppt把差分约束的概念解释得很清楚了,为了以后回看博客能快速想起来差分约束的概念,便把ppt上关键的内容贴出来:
Credits:刘建东学长、黄瑞哲学长。
所以,差分约束的核心思想就是将问题转化为图论的单源最短/最长路径问题。
二、解题
1.将问题转化为图论的单源最长路径问题
sum[i] = k:数轴上[0, i]之间选k个点
由于[ai, bi] 里至少有 ci 个点,故需要满足:sum[bi+1] - sum[ai] ≥ ci
移项,得:
sum[bi+1] ≥ ci + sum[ai]
故令边(bi+1, ai)的边权为ci。
需要注意的是:0 ≤ sum[i] - sum[i-1] ≤ 1
移项,得:
sum[i] ≥ sum[i-1]
sum[i-1] ≥ sum[i] - 1
故令边(i, i-1)的边权为0、边(i-1, i)的边权为-1。
需要注意的是,移项后是“≥”号,故问题被转化为了图论的单源最长路径问题。
最后取最右端的bi的sum作为答案。
2.求解单源最长路径问题
前面学过的SPFA算法,能很好地解单源最短/最长路径问题,故采用了SPFA算法。
代码
#include <iostream>
#include <queue>
#include <cstring>
#include <cmath>
#define Inf 100000
#define MAX_V 500500
#define MAX_E 500500
using namespace std;
int n;
int a, b, c;
int Right;
struct node
{
int to;
int weight;
int next;
}Edges[MAX_E];
int Head[MAX_V];
int tot;
int dis[MAX_V];
int inq[MAX_V];
int cnt[MAX_V];
bool visit[MAX_V];
queue<int> q;
void add(int u, int v, int w)
{
tot++;
Edges[tot].to = v;
Edges[tot].weight = w;
Edges[tot].next = Head[u];
Head[u] = tot;
}
void SPFA(int s)
{
for (int i = 1; i <= n; i++) {
dis[i] = -1 * Inf;
}
dis[s] = 0;
inq[s] = 1;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
inq[u] = 0;
for (int i = Head[u]; i != -1; i = Edges[i].next) {
int v = Edges[i].to;
int w = Edges[i].weight;
if (dis[v] < dis[u] + w) {
dis[v] = dis[u] + w;
if (!inq[v] && !visit[v]) {
q.push(v);
inq[v] = 1;
}
}
}
}
}
void init()
{
memset(Head, -1, sizeof(Head));
memset(inq, 0, sizeof(inq));
memset(cnt, 0, sizeof(cnt));
memset(visit, false, sizeof(visit));
tot = -1;
Right = -1;
}
int main()
{
scanf("%d", &n);
init();
for (int i = 0; i < n; i++) {
scanf("%d%d%d", &a, &b, &c);
if (Right < b) Right = b;
add(a, b + 1, c);
}
Right++;
for (int i = 1; i <= Right; i++) {
add(i - 1, i, 0);
add(i, i - 1, -1);
}
SPFA(0);
printf("%d", dis[Right]);
return 0;
}
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
#define MAX_V 50500
#define MAX_E 50500
#define Inf 10000000
using namespace std;
int n;
int a, b, c;
struct edge {
int to;
int weight;
edge(int theTo, int theWeight) :to(theTo), weight(theWeight) {}
};
vector<edge> H[MAX_E];
int dis[MAX_V];
bool inq[MAX_V];
queue<int> q;
int Right;
void SPFA(int s)
{
inq[s] = true;
dis[s] = 0;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
inq[u] = 0;
for (int i = 0; i < H[u].size(); i++) {
int v = H[u][i].to;
int w = H[u][i].weight;
if (dis[v] < dis[u] + w) {
dis[v] = dis[u] + w;
if (!inq[v]) {
q.push(v);
inq[v] = 1;
}
}
}
}
}
void init()
{
for (int i = 0; i <= MAX_V; i++) {
dis[i] = -1 * Inf;
inq[i] = false;
}
Right = -1;
}
int main()
{
scanf("%d", &n);
init();
for (int i = 0; i < n; i++)
{
scanf("%d %d %d", &a, &b, &c);
H[a].push_back(edge(b + 1, c));
if (Right < b) Right = b;
}
for (int i = 1; i <= Right + 1; i++)
{
H[i - 1].push_back(edge(i, 0));
H[i].push_back(edge(i - 1, -1));
}
SPFA(0);
Right++;
cout << dis[Right] << endl;
return 0;
}