POJ1201 Intervals (进阶指南, 差分约束,spfa)

算法竞赛进阶指南,394页,差分约束系统, spfa
题目意思:
有n段区间, 起点和终点分别是 a[i], b[i]; 要求在没段区间选 c[i]个数。
求:最少选几个数满足条件;
本题要点:
1、 s[k]表示 从 0 ~ k 之间最少选出多少个数;
那么从 a[i] 和 b[i]之间选c[i] 个数,满足条件 s[b[i]] - s[a[i] - 1] >= c[i],
这个等式符合差分约束系统;
同时,从 0 ~ k 之间选 比从 0 ~ k -1 之间选的数要多, 满足 s[k] - s[k - 1] >= 0;
每个数只选一次, s[k] - s[k - 1] <= 1, 变形为 :s[k - 1] - s[k] >= -1;

2、上面的3条式子,s[b[i]] - s[a[i] - 1] >= c[i], 表示 从 a[i] - 1 到 b[i] 连长度为c[i] 的有向边;
s[k] - s[k - 1] >= 0, 表示 从 k - 1 到 k 连长度为 0 的有向边;
s[k - 1] - s[k] >= -1, 表示 从 k 到 k - 1连长度为 -1 的有向边;
起点为 -1 ,另 s[-1] == 0, 求单源最长路径。 s[maxNum] 就是答案, maxNum 是出现过的最大的下标 b[i]

#include <cstdio>
#include <cstring>
#include <queue>
#include <iostream>
using namespace std;
const int MaxN = 50010;
int n;
int a[MaxN], b[MaxN], c[MaxN];
int head[MaxN], ver[MaxN * 4], edge[MaxN * 4], Next[MaxN * 4], d[MaxN];
bool vis[MaxN];
int tot, maxNum = -1;

void add(int x, int y, int z)
{
	ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot;
}

void spfa()
{
	memset(d, 0x80, sizeof(d));
	memset(vis, false, sizeof(vis));
	d[MaxN - 1] = 0, vis[MaxN - 1] = true;
	queue<int> q;
	q.push(MaxN - 1);
	while(q.size())
	{
		int x = q.front(); q.pop();
		vis[x] = false;
		for(int i = head[x]; i; i = Next[i])
		{
			int y = ver[i], z = edge[i];
			if(d[y] < d[x] + z)
			{
				d[y] = d[x] + z;
				if(!vis[y])
				{
					q.push(y);
					vis[y] = true;
				}
			}
		}
	}
}

int main()
{
	int st = MaxN - 1;
	int x, y, z;
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i)
	{
		scanf("%d%d%d", &a[i], &b[i], &c[i]);
		maxNum = max(maxNum, b[i]);
		x = a[i] - 1;
		y = b[i], z = c[i];
		if(-1 == x)
		{
			x = st;	//原本是以 下标为-1 的点为原点的,用 st 来代替 -1
		}
		add(x, y, z);
	}
	add(st, 0, 0);
	add(0, st, -1);
	for(int i = 1; i <= maxNum; ++i)
	{
		add(i - 1, i, 0);
		add(i, i - 1, -1);
	}
	spfa();
	printf("%d\n", d[maxNum]);
	return 0;
}

/*
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1
*/

/*
6
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值