题目描述#
一个数列A={a1,a2,…,an},你可以选择一个区间[L,R],对于所有的i∈[L,R],将ai增加一。
请问最少操作多少次,可以将数列的元素变成相同的?
比如数列为{1,2,3,4},我们可以通过3次操作将所有的元素都变成4,分别取区间为[1,1],[1,2],[1,3]。
输入格式#
第一行是一个整数T(1≤T≤100),表示样例的个数。 以后每个样例占两行,第一行是整数n(1≤n≤10000),表示数列的大小。第二行是n个整数,所有整数的绝对值小于109。
输出格式#
每行输出一个样例的结果,为一个整数。
样例输入#
2 4 1 2 3 4 4 3 2 4 1样例输出#
3 5
解题思路:把 数列 抽象看成 直方图,每个数的值,即对应 y轴的 坐标。要让所有元素都变成相同的,最简单的方法就是,从左向右扫,如果右边的数大于左边的数, 左边的数 补齐至 右边的数的大小。时间复杂度只有 O(n) .
这里使用一个新的数据结构——栈。 因为整个处理过程就是一个 入栈出栈 的过程。先把第一个数入栈,后面小于栈顶元素的值,都入栈,遇见大于栈顶元素的值,开始出栈(进行+1操作)。具体思路不细说了,找几个样例,模拟下入栈出栈的操作。
熟悉进出栈的同学,是否发现这题不用 栈 也可以写。第22行代码,ans 是不是只加 num[i] - Stack[top], 所以简化代码: 当 右数 > 左数时,ans += (num[i] - num[i-1]), 当 右数 < 左数 时,continue; 不做处理。(当然最后也要有与27行同样作用的处理)
AC代码:
#include <stdio.h>
int main()
{
int T,n;
scanf("%d",&T);
while (T --)
{
int top = 0;
__int64 ans = 0;
int num[10010] = {0}, Stack[10010] = {0};
scanf("%d",&n);
for (int i = 0; i < n; i ++)
scanf("%d",&num[i]);
Stack[++top] = num[0]; // 第一个数 入栈
for (int i = 1; i < n; i ++)
{
if (num[i] < Stack[top]) Stack[++top] = num[i]; // 如果当前数 小于 栈顶元素, 将num[i] 压入栈中
else if (num[i] > Stack[top]) // 如果当前数 大于 栈顶元素
{
ans += (num[i]-Stack[top]); // 前面所有 小于 num[i] 的元素, 需要 num[i]-Stack[top] 步 +1,可以全部变成 num[i]
while (num[i] > Stack[top] && top > 0) top --; // 小于num[i] 的元素 全部出栈
Stack[++top] = num[i]; // 把 num[i] 压入栈中
}
}
if (top > 1) ans += (Stack[1]-Stack[top]); // 如果栈不为空
printf("%I64d\n",ans);
}
return 0;
}