洛谷P5019 [NOIP2018 提高组] 铺设道路(贪心:从50行变成10行?)

前言

前一星期顾着补计网的作业,又遇到了堆排序,搞懂了原理却不太想自己下手敲代码,差点终止我的刷题计划,幸好我先把堆排序放了放,先搞懂原理就行。今天继续咱们的贪心之旅,我计划下周内写动态规划。有想学动态规划的也可以关注一下。

题目

题目描述

春春是一名道路工程师,负责铺设一条长度为 n n n 的道路。

铺设道路的主要工作是填平下陷的地表。整段道路可以看作是 n n n 块首尾相连的区域,一开始,第 i i i 块区域下陷的深度为 d i d_i di

春春每天可以选择一段连续区间 [ L , R ] [L,R] [L,R] ,填充这段区间中的每块区域,让其下陷深度减少 1 1 1。在选择区间时,需要保证,区间内的每块区域在填充前下陷深度均不为 0 0 0

春春希望你能帮他设计一种方案,可以在最短的时间内将整段道路的下陷深度都变为 0 0 0

输入格式

输入文件包含两行,第一行包含一个整数 n n n,表示道路的长度。 第二行包含 n n n 个整数,相邻两数间用一个空格隔开,第 i i i 个整数为 d i d_i di

输出格式

输出文件仅包含一个整数,即最少需要多少天才能完成任务。

样例 #1

样例输入 #1

6   
4 3 2 5 3 5

样例输出 #1

9

提示

【样例解释】

一种可行的最佳方案是,依次选择:
[ 1 , 6 ] [1,6] [1,6] [ 1 , 6 ] [1,6] [1,6] [ 1 , 2 ] [1,2] [1,2] [ 1 , 1 ] [1,1] [1,1] [ 4 , 6 ] [4,6] [4,6] [ 4 , 4 ] [4,4] [4,4] [ 4 , 4 ] [4,4] [4,4] [ 6 , 6 ] [6,6] [6,6] [ 6 , 6 ] [6,6] [6,6]

【数据规模与约定】

对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 10 1 ≤ n ≤ 10 1n10
对于 70 % 70\% 70% 的数据, 1 ≤ n ≤ 1000 1 ≤ n ≤ 1000 1n1000
对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 100000 , 0 ≤ d i ≤ 10000 1 ≤ n ≤ 100000 , 0 ≤ d_i ≤ 10000 1n100000,0di10000

题目分析

  一开始的想法是每次都填充当前最长的可填充空间,这样每次的填充都是最长的,于是想看看案例验证一下,却发现题目提示中的案例提示似乎是按照大小的,于是我又提出了新的想法,就是每次都从序号最小的开始,填充从这个序号开始的最长可填充区间。
  问题是怎么证明这个做法是保证每一步都是最优解的做法?我给一个通俗的解释(可能是正经的解释不会).就是一开始肯定会一直全部都可以直到填到被分隔开,这时还需要继续填充。那么我们就可以从最小的开始,一直到最长的不能填充为止,如果中途停止肯定是不行的;那如果不从最小的开始呢,那么你最后也还是会需要填充最小的,所以天数肯定>=前者,所以我们可以从最小的开始填充。
  但是很可惜就是这种方法还是超时了,最后三个点,但是这个方法还是值得学习的,只要数据量小一些就可以过了。所以我们需要一个更巧妙的方法。(代码附在后话)
  这种方法其实和前面的思想一样,但是他有更巧妙的表格,假设现在有一个坑,但旁边又有一个坑。你肯定会选择把两个同时减1;那么小的坑肯定会被大的坑“带着”填掉。大的坑也会减少a[i]-a[i-1]的深度,可以说是“免费的”,最后就只剩下几个大坑。按照我们之前的想法,其实第一个坑是一定需要深度相同的天数填的a[0],第二个大坑(递增)前面的坑比第一个坑浅的都被填了,只剩下多出来的部分a[i]-a[i-1],同理只要一个坑比前一个坑大都需要加上这额外的部分。所以结果就是sum=a[0]+ Σa[i]-a[i]-1

注意事项

1.天数和数组要开大一点
2.好像没有其他注意事项了

代码

耶


#include<iostream>
using namespace std;
int main(){
	int n,a[100007];
	long long day=0; 
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>a[i];
		if(a[i]>a[i-1]&&i>0)
			day+=a[i]-a[i-1]; }
	cout<<day+a[0]<<endl;
	return 0;}

后话

第一种方法最后三个点TLE了,不过思想是可以借鉴的
哭了
注意下面代码只能过七个点!!!

#include<iostream>
using namespace std;
 
int main(){
	int n,a[100007],day=0;
	int count=0;//count代表坑的总深度 
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>a[i];
		count+=a[i];
	}
	//int start=0;//start代表目前第一块还没有填平的 
	while(count!=0){
		for(int i=0;i<n;i++){//缺点:每次都会重复已经填充的格子 
			if(a[i]){
				for(int j=i;j<n;j++){//从i开始找到连续的凹陷 
					if(a[j]){
						a[j]--;
						count--; 
					} 
					else{
						break;
					}
				}
				day++;
				break;
			} 
		}
	}
	cout<<day<<endl;
	return 0;
}

题目来源

NOIP2018 提高组 D1T1
洛谷链接

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值