嘤,总算过了
题目大意:可以从一个序列中按照顺序(可间断)选出一堆数,选出的这些数可以做以下操作:
- 奇数位置 + 1 +1 +1,偶数位置 − 1 - 1 −1
- 偶数位置 + 1 +1 +1,奇数位置 − 1 - 1 −1
直到整个序列变成全 0 0 0,停止。问整个过程所需要的最小操作次数
这个题我一开始想的是就用原数组做,构造出一个最长的正负交错的序列,比如现在栈里面如果有一个负数,那就应该在接收一个正数,如果再遇到一个负数就和栈里的负数比较,哪个小要哪个。以栈中最小的数对所有数进行处理,重复上述过程直到所有元素为
0
0
0.这个过程复杂度取决于元素的大小。但是如果考虑这个序列:
−
1
,
2
,
−
3
,
4
,
−
5
,
.
.
.
.
-1,2,-3,4,-5,....
−1,2,−3,4,−5,....这样的序列一定会超时,如果考虑处理完栈中的元素对其清0,下次操作未必是最长的(未必最优)
下面的思路参考文章:
文章1
文章2
求一个序列的前缀和数组,那么如果原来的数组全
0
0
0就应该等价于前缀和数组全0.为啥要转换成前缀和数组?他有这样的好处,我们来看:
由于是在原序列中可以按照顺序随意间隔取数,转换在前缀和数组就是随意
+
1
,
−
1
+1,-1
+1,−1。(这个随意不是指
+
1
,
−
1
+1,-1
+1,−1同时进行,而是指
+
1
+1
+1操作可以随意进行,
−
1
-1
−1操作可以随意进行。)而原数组相当于是前缀和数组的差分数组(差分数组介绍:点我)所以在前缀数组中
+
1
+1
+1,在差分数组中会自动弄出一个
+
1
,
−
1
+1,-1
+1,−1
(前后:
a
,
b
,
c
;
a,b,c;
a,b,c;差分数组:
a
,
d
e
l
t
a
1
,
d
e
l
t
a
2
;
a,delta1,delta2;
a,delta1,delta2;
a
,
b
+
1
,
c
;
a,b+1,c;
a,b+1,c;差分数组:
a
,
d
e
l
t
a
1
+
1
,
d
e
l
t
a
2
−
1
a,delta1+1,delta2-1
a,delta1+1,delta2−1)
我们假设对前缀数组中的元素是这样操作的:
1,0,0,1,0,0,1
则在原数组中是:
1,-1,0,1,-1,0,1(符合原要求)
如果在前缀数组中这样做:
1,1
原数组:
1,0
这样的话,只要计算出前缀和的最大值和最小值就可以了, 最大值 − 最小值 最大值-最小值 最大值−最小值一定是最小的操作次数,不过要注意初始最大值和最小值为0.如果全都是负数的话,那么最优操作是将最小负数提上来。 最大值 − 最小值 最大值-最小值 最大值−最小值是在最大值为正数,最小值为负数的情况下成立的,为了把正数拉下来,把负数抬上去。
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define min(a,b) (a<b)?a:b
#define max(a,b) (a>b)?a:b
typedef long long ll;
const int length = 2e5 + 5;
ll num[length];
ll qianzhui[length];
int main(void)
{
int t;
scanf_s("%d", &t);
for (int i = 0; i < t; i++)
{
int n;
scanf_s("%d", &n);
ll min1 = 0;
ll max1 = 0;
for (int i = 0; i < n; i++)
{
scanf_s("%lld", &num[i]);
if (i == 0)
{
qianzhui[i] = num[i];
}
else
qianzhui[i] = qianzhui[i - 1] + num[i];
min1 = min(min1, qianzhui[i]);
max1 = max(max1, qianzhui[i]);
}
printf("%lld\n", max1 - min1);
}
}