Description
Time Limit: 1sec Memory Limit:256MB
某人想检验最近新研发的清洁工具的效果,便找来一块布满污渍的长木板。长木板上的污渍厚度不一。
清洁工具每次的工作原理是这样的:其矩形探头可以伸缩任意长度,然后选取木板上的一段区间进行去污,之后这段区间所有的污渍厚度减一。注意不能向木板中无污渍的地方去污,否则会导致木板损坏。问最少需要多少次才能清洁木板
或者我们可以将其抽象成这样的模型:给出木板长度n以及每段污渍的厚度a1,a2,…,an,每次操作可以选取区间 [l,r] (1<=l<=r<=n,ai>0,l<=i<=r),之后将这个区间上的所有数都减一,但需保证这段区间的数操作之前均大于0。问使得a1,a2,…,an均为0最少需要多少次操作。
Input
第一行一个正整数T(<=20),表示数据的个数
接下来T组数据,第一行为木板的长度n(<=100)
接下来n个非负整数ai(<=100),表示距离为i处的污渍厚度
Output
每组数据输出一行,一个数,表示最少需要清洁的次数
Sample Input
2
5
2 4 1 2 3
2
3 4
Sample Output
6
4
Hint
第一组数据解释:第一次清理[1,5],剩下1,3,0,1,2
第二次清理[1,2],第三次与第四次清理[2,2],第五次清理[4,5],最后一次清理[5,5]。
一共六次,可以证明这是最少的次数。
分析
可以知道木板的污渍厚度其实是一个波峰图,我们要找的就是每个波峰,以及处在下降和上升之间的转折点。因为规律为:最少的清洁次数为波峰的和减去转折点值之和所得的差。有兴趣的同学可以自己画画图多试几遍。
for(int i = 0;i < n;i++){
if(arr[i] >= maxNum[index]){//找升序
maxNum[index] = arr[i];
}
else{
while(arr[i+1] <= arr[i]&& i+1 < n){//找降序
i++;
}
right[index] = arr[i];//找到分界
index++;
maxNum[index] = arr[i];
}
}
要特别注意的是,最后一组的波峰有可能刚好是最后一个数据,所以我们要判断,并且最后总和那里也要处理好。
if(right[index] == 0){
right[index] = arr[n-1];//最后一个数字为最后一个组合的最大数
}
int sum = 0;
for(int i = 1;i < index;i++){
sum += maxNum[i];
sum -= right[i];
}
sum += maxNum[index];
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
int n;
int main(){
int T;
cin>>T;
for(int t = 0;t < T;t++){
cin>>n;
int arr[n];
//<int> vec;
for(int i = 0;i < n;i++){
cin>>arr[i];
//arr.push_back();
}
int maxNum[n],right[n+1];
memset(maxNum,0,sizeof(maxNum));
memset(right,0,sizeof(right));
right[0] = 0;
int index = 1;
maxNum[index] = arr[0];
for(int i = 0;i < n;i++){
if(arr[i] >= maxNum[index]){//找升序
maxNum[index] = arr[i];
}
else{
while(arr[i+1] <= arr[i]&& i+1 < n){//找降序
i++;
}
right[index] = arr[i];//找到分界
index++;
maxNum[index] = arr[i];
}
}
if(right[index] == 0){
right[index] = arr[n-1];//最后一个数字为最后一个组合的最大数
}
int sum = 0;
for(int i = 1;i < index;i++){
sum += maxNum[i];
sum -= right[i];
}
sum += maxNum[index];
cout<<sum<<endl;
}
}