【SOJ 639】种树

【题目】

题目描述:

为了绿化乡村,H 村积极响应号召,开始种树了。

H 村里有 n 幢房屋,这些屋子的排列顺序很有特点,在一条直线上。于是方便起见,我们给它们标上 1~n 。树就种在房子前面的空地上。

同时,村民们向村长提出了 m 个意见,每个意见都是按如下格式:希望第 li 个房子到第 ri 个房子的房前至少有 ci 棵树。

因为每个房屋前的空地面积有限,所以每个房屋前最多只能种 ki 棵树。

村长希望在满足村民全部要求的同时,种最少的树以节约资金。请你帮助村长。

输入格式:

输入文件输入第 1 行,包含两个整数 n,m 。
第 2 行,有 n 个整数 ki。
第 2~m+1 行,每行三个整数 li,ri,ci 。

输出格式:

输出 1 个整数表示在满足村民全部要求的情况下最少要种的树。村民提的要求是可以全部满足的。

样例数据:

【样例1】

输入

5 3
1 1 1 1 1
1 3 2
2 4 2
4 5 1

输出

3

【样例2】

输入

4 3
3 2 4 1
1 2 4
2 3 5
2 4 6

输出

8

备注:

【样例1解释】 
如图是满足样例的其中一种方案,最少要种 3 棵树。

【样例2解释】
如图是满足样例的其中两种方案,左图的方案需要种 9 棵树,右图的方案需要种 8 棵树。可以验证,最少需要种 8 棵树。

【数据范围】
对于 30% 的数据,0 < n ≤ 100,0 < m ≤ 100,ki = 1; 
对于 50% 的数据,0 < n ≤ 2,000,0 < m ≤ 5,000,0 < ki ≤ 100; 
对于 70% 的数据,0 < n ≤ 50,000,0 < m ≤ 100,000,0 < ki ≤ 1,000; 
对于 100% 的数据,0 < n ≤ 500,000,0 < m ≤ 500,000,0 < ki ≤ 5,000

【分析】

这道题应该算是比较裸的差分约束模板题吧

还是用前缀和的思想,即用 d[ i ] 表示前 i 个(包括 i)幢房屋中最少要种的树

那么首先有下面两个限制:

  • d[ i - 1 ] ≤ d[ i ],转换成 d[ i ] - d[ i - 1 ] ≥ 0
  • d[ i ] - d[ i - 1 ] ≤ k[ i ],转换成 d[ i - 1 ] - d[ i ] ≥ -k[ i ]

假如现在有一条 l 到 r 之间最少有 c 课树的限制,那么:

  • d[ r ] - d[ l - 1 ] ≥ c

按照这些限制建完图之后,跑一边 SPFA 就行了,由于是前缀和,最后的答案就是 d[ n ]

备注一点,因为这道题是求最小值,我们要用 SPFA 跑最长路

【代码】

#include<queue>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 500005
#define M 2000005
using namespace std;
int Read()
{
	int x=0;
	char c=getchar();
	while(!isdigit(c))
	  c=getchar();
	while(isdigit(c))
	{
		x=(x<<1)+(x<<3)+(c^'0');
		c=getchar();
	}
	return x;
}
int t,d[N],first[M];
int v[M],w[M],next[M];
bool vis[N];
void add(int x,int y,int z)
{
	t++;
	next[t]=first[x];
	first[x]=t;
	v[t]=y;
	w[t]=z;
}
void spfa(int s)
{
	int x,y,i,j;
	memset(d,128,sizeof(d));
	memset(vis,false,sizeof(vis));
	d[s]=0;
	vis[s]=true;
	queue<int>q;
	q.push(s);
	while(!q.empty())
	{
		x=q.front();
		q.pop();
		vis[x]=false;
		for(i=first[x];i;i=next[i])
		{
			y=v[i];
			if(d[y]<d[x]+w[i])
			{
				d[y]=d[x]+w[i];
				if(!vis[y])
				{
					q.push(y);
					vis[y]=true;
				}
			}
		}
	}
}
int main()
{
	int n,m,i,x,l,r,c;
	n=Read();
	m=Read();
	for(i=1;i<=n;++i)
	{
		x=Read();
		add(i-1,i,0);
		add(i,i-1,-x);
	}
	for(i=1;i<=m;++i)
	{
		l=Read();
		r=Read();
		c=Read();
		add(l-1,r,c);
	}
	spfa(0);
	printf("%d",d[n]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_dreams/article/details/81665678
今日推荐