前言
前一星期顾着补计网的作业,又遇到了堆排序,搞懂了原理却不太想自己下手敲代码,差点终止我的刷题计划,幸好我先把堆排序放了放,先搞懂原理就行。今天继续咱们的贪心之旅,我计划下周内写动态规划。有想学动态规划的也可以关注一下。
题目
题目描述
春春是一名道路工程师,负责铺设一条长度为 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
1≤n≤10 ;
对于
70
%
70\%
70% 的数据,
1
≤
n
≤
1000
1 ≤ n ≤ 1000
1≤n≤1000 ;
对于
100
%
100\%
100% 的数据,
1
≤
n
≤
100000
,
0
≤
d
i
≤
10000
1 ≤ n ≤ 100000 , 0 ≤ d_i ≤ 10000
1≤n≤100000,0≤di≤10000 。
题目分析
一开始的想法是每次都填充当前最长的可填充空间,这样每次的填充都是最长的,于是想看看案例验证一下,却发现题目提示中的案例提示似乎是按照大小的,于是我又提出了新的想法,就是每次都从序号最小的开始,填充从这个序号开始的最长可填充区间。
问题是怎么证明这个做法是保证每一步都是最优解的做法?我给一个通俗的解释(可能是正经的解释不会).就是一开始肯定会一直全部都可以直到填到被分隔开,这时还需要继续填充。那么我们就可以从最小的开始,一直到最长的不能填充为止,如果中途停止肯定是不行的;那如果不从最小的开始呢,那么你最后也还是会需要填充最小的,所以天数肯定>=前者,所以我们可以从最小的开始填充。
但是很可惜就是这种方法还是超时了,最后三个点,但是这个方法还是值得学习的,只要数据量小一些就可以过了。所以我们需要一个更巧妙的方法。(代码附在后话)
这种方法其实和前面的思想一样,但是他有更巧妙的表格,假设现在有一个坑,但旁边又有一个坑。你肯定会选择把两个同时减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
洛谷链接