铺设道路
【问题描述】
春春是一名道路工程师,负责铺设一条长度为 n 的道路。
铺设道路的主要工作是填平下陷的地表。整段道路可以看作是 n 块首尾相连的区
域,一开始,第 i 块区域下陷的深度为
d
i
d i
di 。
春春每天可以选择一段连续区间
[
L
,
R
]
[L,R]
[L,R],填充这段区间中的每块区域,让其下陷深
度减少 1。在选择区间时,需要保证,区间内的每块区域在填充前下陷深度均不为 0 。
春春希望你能帮他设计一种方案,可以在最短的时间内将整段道路的下陷深度都变
为 0 。
【输入格式】
输入文件名为 road.in 。
输入文件包含两行,第一行包含一个整数 n,表示道路的长度。
第二行包含 n 个整数,相邻两数间用一个空格隔开,第 i 个整数为 d i 。
【输出格式】
输出文件名为 road.out 。
输出文件仅包含一个整数,即最少需要多少天才能完成任务。
【输入输出样例 1】
road.in road.out
6
4 3 2 5 3 5
9
见选手目录下的 road/road1.in 和 road/road1.ans 。
【样例解释】
一种可行的最佳方案是,依次选择:
[1,6]、[1,6]、[1,2]、[1,1]、[4,6]、[4,4]、[4,4]、[6,6]、[6,6]。
【输入输出样例 2】
见选手目录下的 road/road2.in 和 road/road2.ans 。
【数据规模与约定】
3;
70% 的数据,1 ≤ ? ≤ 10;
对于 100% 的数据,1 ≤ ? ≤ 100000 ,0 ≤ d i ≤ 10000 。
【解题思路】
思路一:贪心思想
从左到右依次遍历数组,为了尽可能快的填充所有的坑,先将第一个坑填掉。如果第二个坑的深度小于等于第一个坑的深度,则在填充第一个坑的时候,第二个坑也一并被填掉了;如果第三个坑小于等于第二个坑,那么在填充第二个坑的时候,第三个坑也一并被填充掉了……以此类推,只要后面一个坑的深度小于等于前面一个坑的深度,那么就可以跟第一个坑一并填充,否则必须单另填充它。
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
const int maxn=1e5+5;
int n,a[maxn],ans;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
ans+=a[1];//处理第一个坑
for(int i=2;i<=n;i++)
if(a[i]>a[i-1]) ans+=a[i]-a[i-1];//按规则处理后面的
printf("%d\n",ans);
return 0;
}
思路二:递归解决
每次我们从当前序列当中选择一个深度最小的坑进行处理,处理完成之后,当前序列有可能当前序列被0分割成若干的子序列,那么继续处理这些子序列,最后全部处理完即可得到答案。
寻找最小值的位置可以使用RMQ算法来优化。
优化前代码
#include<cstdio>
const int maxn=1e5+5;
int n,a[maxn],ans;
int dg(int l,int r,int d)//[l,r]为填充区间,d为已经填充的天数
{
if(l>r)
return 0;
int p=l;
for(int i=l+1;i<=r;i++)//找出最小值的坑编号p
{
if(a[i]<a[p])
p=i;
}
ans+=a[p]-d;
dg(l,p-1,a[p]);
dg(p+1,r,a[p]);
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
printf("%d\n",dg(1,n,0));
return 0;
}
优化后代码
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
const int maxn=1e5+5;
int n,a[maxn],ans,dp[maxn][20];
void ST(int n)//预处理
{
for (int i = 1; i <= n; i++)
dp[i][0] = i;
for (int j = 1; (1 << j) <= n; j++) {
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
int ls=dp[i][j-1],lr=dp[i+(1<<j-1)][j-1];
if(a[lr]>a[ls]) dp[i][j]=ls;
else dp[i][j]=lr;
}
}
}
int RMQ(int l, int r)
{
int k = log(r-l+1) / log(2);//确定区间长度2^(2k),k为幂
int lr=dp[l][k],ls=dp[r - (1 << k) + 1][k];
if(a[lr]>a[ls]) return ls;
else return lr;
}
int dg(int l,int r,int d)
{
if(l>r)
return 0;
int p=RMQ(l,r);
ans+=a[p]-d;
dg(l,p-1,a[p]);
dg(p+1,r,a[p]);
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
ST(n);
printf("%d\n",dg(1,n,0));
return 0;
}