Week8 作业 A

题目描述:

给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点;

输入输出规模及约定:

输入第一行一个整数 n 表示区间的个数,接下来的 n 行,每一行两个用空格隔开的整数 a,b 表示区间的左右端点。

1 <= n <= 50000, 0 <= ai <= bi <= 50000 并且1 <= ci <= bi - ai+1。

输出一个整数表示最少选取的点的个数

思路:

关于差分约束系统:https://blog.csdn.net/my_sunshine26/article/details/72849441 ,这篇博文写的挺好,不再搬运了。

如果我们用sum[i]表示在i及之前,选了多少了个点,则下述差分约束成立:

sum[bi+1] - sum[a] >= ci

0=<sum[i+1] - sum[i] <=1

然后跑最长路,sum[ max{bi} ]就是结果;

值得注意的点:

1、用sum[b+1] - sum[a] 而不是 sum[b] - sum[a-1] ,因为我们图的顶点通常不用负值。相当于把数轴(所有区间)向右移了一个单位。最后求bi的max的时候,每个bi必须加1

2、同理用sum[i+1] - sum[i] 而不是 sum[i] -sum[i-1] 

3、存储边的数组要开的足够大,该结论可以通过仔细分析差分约束条件得出。

代码:

#include <cstdio>
#include <iostream>
#include <queue>
#include <climits>	//防止出现负的下标,转换成sum[b+1]-sum[a]>=c 
using namespace std;
const int MAXN=5e4+5;
struct e
{
	int v,w,next;
}Edge[3*MAXN];		//值得注意的是边数 
int last[MAXN];	//因为可能有零点,last数组要初始化成-1 
int tot;
int dis[MAXN],inq[MAXN];
int maxb=-1;
int mina=INT_MAX;
void addEdge(int u,int v,int w)
{
	tot++;
	Edge[tot].v=v;
	Edge[tot].w=w;
	Edge[tot].next=last[u];
	last[u]=tot;
}
void SPFA()
{
	for(int i=mina;i<=maxb;i++)
		dis[i]=-INT_MAX;
	queue<int> q;
	q.push(mina);
	inq[mina]=1;
	dis[mina]=0;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		inq[u]=0;
		for(int i=last[u];i!=-1;i=Edge[i].next)
		{
			int v=Edge[i].v,w=Edge[i].w;
			if(dis[v]<dis[u]+w)
			{
				dis[v]=dis[u]+w;
				if(inq[v]==1) continue;
				else inq[v]=1,q.push(v);
			}
		}
	}
}
int main()
{
	int N; cin>>N;

	for(int i=0;i<MAXN;i++)	last[i]=-1; 
	for(int i=1;i<=N;i++)
	{
		int u,v,w;
		scanf("%d %d %d",&u,&v,&w);
		maxb=max(v+1,maxb);
		mina=min(u,mina);
		addEdge(u,v+1,w);   //(v)-(u-1)>=c 
	}
	for(int i=mina;i<=maxb;i++)
	{
		addEdge(i+1,i,-1);
		addEdge(i,i+1,0); 
	}
	SPFA();
	cout<<dis[maxb]<<endl;
	return 0;
}

  

原文地址:https://www.cnblogs.com/qingoba/p/12720325.html