week8-A-区间选点(差分约束的应用)

题意:

给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ a i a_i ai, b i b_i bi] 里至少有 c i c_i 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

思路:

用sum[i]表示数轴上[0, i] 之间选点的个数,对于第 i 个区间,sum[ b i b_i bi] - sum[ a i a_i ai] >= c i c_i ci,而且对于任意 i > 0,0 <= sum[i] - sum[i - 1] <= 1。
这两个式子可以类比差分约束标准式 x i x_i xi - x 1 x_1 x1 <= T 类似,所以可以使用差分约束来解决问题。
对于sum[ b i b_i bi] - sum[ a i a_i ai - 1] >= c i c_i ci,可以变形为有向边 a i a_i ai-1-> b i b_i bi,权值为 c i c_i ci。0 <= sum[i] - sum[i - 1] <= 1,可以变形为有向边 i-1->i,边权为0,以及 i->i-1,边权为-1。
构造出有向加权图之后,对该图进行spfa操作,得到sum[bMax],并输出。
注意事项: a i a_i ai>=0,所以 a i a_i ai-1可能小于0,所以有向边构造时,需要把 a i a_i ai-1-> b i b_i bi改为 a i a_i ai-> b i b_i bi+1。

代码:

#include <iostream>
#include <vector>
#include <queue>
#include <cstring> 
using namespace std;

struct edge {
	int to, weight;
	edge(){}
	edge(int t, int w) {
		to = t;
		weight = w;
	}
};

int inf = (int)1e9;
const int N = 50005;
vector<edge> g[N];
int dis[N], inq[N];
int mx = 0, mn = inf;//所有区间的最右边界和最左边界
int n;

void spfa(int x) {
	//初始化
	memset(dis, 0, sizeof(dis));
	memset(inq, 0, sizeof(inq));
	//队列
	queue<int> q;
	//第一个元素入队
	q.push(x);
	dis[x] = 0;
	inq[x] = 1;
	//循环,取出队列中的front,进行判断
	while (q.size()) {
		//取出front
		int now = q.front(); q.pop(); inq[now] = 0; //cout << now <<" "; 
		//每次取出now指向的第i个点
		for (int i = 0; i < g[now].size(); i++) {
			int y = g[now][i].to;
			int w = g[now][i].weight;
			//判断修改
			if (dis[y] == 0 && dis[now] == 0 && w == 0){
				if (!inq[y]) {
					q.push(y);
					inq[y] = 1;
				} 
			} 
			if (dis[y] < dis[now] + w) {
				dis[y] = dis[now] + w; //cout << y << " " << dis[y] << endl;
				//判断入队
				if (!inq[y]) {
					q.push(y);
					inq[y] = 1;
				}
			}
		}
	}
}

int main()
{
	cin >> n;
	for (int i = 0; i < n; i++) {
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		g[a].push_back(edge(b + 1, c));
		mn = min(a, mn);
		mx = max(b + 1, mx);
	}
	for (int i = mn; i < mx; i++) {
		g[i].push_back(edge(i + 1, 0));
		g[i + 1].push_back(edge(i, -1));
	}//cout << mn << mx << endl;
	spfa(mn);
	cout << dis[mx];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值