【差分约束】intervals

题目描述

一个整数集合Z有n个区间,每个区间有3个值,ai,bi,ci代表,在区间[ai,bi]上至少有ci个整数属于集合Z,ci可以在区间内任意取不重复的点。
现在要满足所有区间的约束条件,问最少可选多少个点。

输入

第一行一个整数n,表示区间个数

以下n行描述区间,第i+1行,三个整数ai,bi,ci,由空格隔开,其中 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

提示

这题要注意的点是,我们求的是区间内的最少点数,数组dist[i]表示从头直到第i个点(包括i)中点的数目,每组 ai,bi,ci表示[ai,bi]=ci,即dist[b]-dist[a-1]=ci,但由于a的数据可能取到0,a-1就会小于0,所以我们把所有区间数字增加1,就变成了dist[b+1]-dist[a]=c,然后我们就建一条a->b+1的权值为c的有向边。

对于每一个点i,在初始的情况下都处于可选可不选,即dist[i+1]-dist[i]=0/1,所以每一个i都要建两条边i->i+1权值为0的有向边和i+1->权值为-1的有向边。那么为什么不是建i-i+1权值为1和i+1->i权值为0的两条边呢,这时因为在spfa中,如果建了i-i+1权值为1这条边,在i点可以不选的时候,很有可能因为dist[v]<dist[u]+w(w=1)而选更新这个数组,导致i点被选中,不仅如此,还会直接影响到后面的dist数组,违反了题目求最小的原则。而换成i+1->权值为-1这条边,在i点可以不选的时候,选这条边只会让dist[u]+w减小,dist不会更新,所以这个时候i点就不会被选中。

 

#include<iostream>
#include<queue>
#include<cstdio>
using namespace std;
#define ll long long
const int maxn=2e5+10;
const int INF=0x7f7f7f7f;
int n,maxnum=0,tot=0,head[maxn],dist[maxn],vis[maxn],in[maxn];
struct Edge
{
	int to,weight,next;
}edge[maxn*2];
void add_edge(int from,int to,int w)
{
	edge[++tot].to=to;
	edge[tot].next=head[from];
	edge[tot].weight=w;
	head[from]=tot;
}
void spfa()
{
	for(int i=0;i<=maxnum;i++)
		dist[i]=-INF;
	queue<int>q;
	q.push(0);
	vis[0]=1;
	dist[0]=0;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		vis[u]=0;
		for(int i=head[u];i;i=edge[i].next)
		{
			int v=edge[i].to,w=edge[i].weight;
			if(dist[v]<dist[u]+w)//求最小值跑最长路径
			{
				dist[v]=dist[u]+w;
				if(!vis[v])//如果v没有入队
				{
					q.push(v);
					vis[v]=1;
				}
			}
		}
	}
}
int main()
{
	scanf("%d",&n);
	while(n--)
	{
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		/*
		依照题意:dist[b]-dist[a-1]=c;但是a-1可能会取到-1;
		所以将所有数自增,就是dist[b+1]-dist[a]=c;
		*/
		if(b+1>maxnum)
			maxnum=b+1;
		add_edge(a,b+1,c);
	}
	for(int i=0;i<=maxnum;i++)
	{
		add_edge(i,i+1,0);//dist[i+1]-dist[i]=0/1;i+1点可选可不选
		add_edge(i+1,i,-1);
	}
	spfa();
	cout<<dist[maxnum]<<endl;
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值