362. 区间(差分约束,spfa)

给定 n 个区间 [ai,bi] 和 n 个整数 ci。

你需要构造一个整数集合 Z,使得 ∀i∈[1,n],Z 中满足 ai≤x≤bi 的整数 x 不少于 ci 个。

求这样的整数集合 Z 最少包含多少个数。

输入格式

第一行包含整数 n。

接下来 n 行,每行包含三个整数 ai,bi,ci。

输出格式

输出一个整数表示结果。

数据范围

1≤n≤500001
0≤ai,bi≤50000
0≤ci≤bi−ai+1

输入样例:
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1
输出样例:
6

解析: 

差分约束系统 是一种特殊的 n 元一次不等式组,它包含 n 个变量 x1,x2,……,xn 以及 m 个约束条件,每个约束条件是由两个其中的变量做差构成的,形如 xi-xj<=ck,其中 1 <= i, j <= n, i != j, 1 <= k <= m 并且 ck 是常数(可以是非负数,也可以是负数)。我们要解决的问题是:求一组解 x1=a1,x2=a2,……,xn=an,使得所有的约束条件得到满足,否则判断出无解。

差分约束系统中的每个约束条件 xi-xj<= ck 都可以变形成 xi <= xj+ck,这与单源最短路中的三角形不等式 dist[y]<= dist[x]+z 非常相似。因此,我们可以把每个变量 xi 看做图中的一个结点,对于每个约束条件 xi-xj<= ck,从结点 j 向结点 i 连一条长度为 ck 的有向边。

注意到,如果 {a1,a2,……,an} 是该差分约束系统的一组解,那么对于任意的常数 d,{a1+d,a2+d,……,an+d\} 显然也是该差分约束系统的一组解,因为这样做差后 d 刚好被消掉。

对于条件 xi<=xj+ck,我们可以通过上述方法建图求最短路,无负环则有解;而对于 xi>=xj+ck,我们可以转换为 xj-xi<=-ck 然后求最短路,也可以直接求最长路(正环有解)。 

由此可以知道:差分约束算法参考自最短路算法

差分约束最难的地方是找不等式关系。

本题可以利用前缀和的思想。因此我们不希望0号点被用到,所以我们可以给每个数+1,这样范围就成1~50001。

S[i]表示1~i中被选出的数的个数,显然S[5001]的最小值为答案。

s[i]需要满足:

1)s[i]>=s[i-1] ,1<=i<=5001

2)s[i]-s[i-1] <=1 ,等价于: s[i-1]>=s[i]-1

3)s[b]-s[a-1]>=c

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
#include<sstream>
#include<deque>
#include<unordered_map>
using namespace std;
typedef long long LL;
const int N = 5e4 + 5, M = N*3 + 5;
int n;
int h[N], e[M], w[M], ne[M], idx;
int q[N], dist[N], vis[N];

void add(int a, int b, int c) {
	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}

void spfa() {
	//cout << "KKKKKKKKKKKKKKKKKKKKKKKKKKKK" << endl;
	memset(dist, -0x3f, sizeof dist);
	int hh = 0, tt = 1;
	q[0] = 0;
	dist[0] = 0;
	vis[0] = 1;
	while (hh != tt) {
		int t = q[hh++];
		if (hh == N)hh = 0;
		vis[t] = 0;
		for (int i = h[t]; i != -1; i = ne[i]) {
			int j = e[i];
			if (dist[j] < dist[t] + w[i]) {
				dist[j] = dist[t] + w[i];
				if (!vis[j]) {
					vis[j] = 1;
					q[tt++] = j;
					if (tt == N)tt = 0;
				}
			}
		}
	}
}

int main() {
	cin >> n;
	memset(h, -1, sizeof h);
	for (int i = 1; i <= 50001; i++) {
		add(i - 1, i, 0);
		add(i, i - 1, -1);
	}
	for (int i = 1,a,b,c; i <= n; i++) {
		scanf("%d%d%d", &a, &b, &c);
		a++, b++;
		add(a-1, b, c);
	}

	spfa();
	cout << dist[50001] << endl;
	return 0;
}

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值