程序设计思维 A - 区间选点 II(差分约束)

题目

给定一个数轴上的 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值