这道题的难度完爆前九道题…
1.分析题目条件
题中允许对高阶圣堂武士进行的变换操作是 a[ i - 1 ] += a[ i ],a[ i + 1 ] += a[ i ],a[ i ] = - a[ i ] ,i 属于 [2 , n-1]
需要通过若干次变换操作,得到最小的 max{ | a[ i ] | }
这个变换操作很复杂,不易求解。
2.前缀和形式
尝试转化为前缀和形式,a[ 1 ] ~ a[ n ] 求取前缀和,记为 s[ 1 ] ~ s[ n ]
此时的 a[ 1 ] ~ a[ n ] 变为 s[ 1 ],s[ 2 ] - s[ 1 ],s[ 3 ] - s[ 2 ] …… s[ n ] - s[ n - 1 ]
看看转化后的变换操作:
如对 i = 2 使用变换操作 ,那么 s[ 1 ] —> s[ 2 ],s[ 2 ] —> s[ 1 ],s[ 3 ] —> s[ 3 ]
发现对 i 进行变换操作,会导致 s[ i - 1 ] 和 s[ i ] 交换
如此,前缀和形式的变换操作是不是简单的多了!
3.完善前缀和数列
经上分析,我们可以在 s[ 1 ] ~ s[ n - 1 ] 之间的数进行任意交换,使 s[ 1 ],s[ 2 ] - s[ 1 ],s[ 3 ] - s[ 2 ]…,s[ n ] - s[ n - 1 ] 之间差的绝对值的最大值最小
等等!只有第一项是 s[ 1 ] 的形式,后面都是相减的形式,这是不是很难受
补上 s[ 0 ] = 0
这样,我们的题目就转化成为了
在 s[ 1 ] ~ s[ n - 1 ] 之间的数进行任意交换,使 s[ 1 ] - s[ 0 ],s[ 2 ] - s[ 1 ],s[ 3 ] - s[ 2 ]…,s[ n ] - s[ n - 1 ] 之间差的绝对值的最大值最小
4.求解
如果 s[ 0 ] ~ s[ n ] 之间都能随意交换,那差的绝对值的最大值最小显然是排序后,这时数列单调,两个数之差必然是最小哒。
但条件固定了 s[ 0 ] 和 s[ n ] 这两个点!
这意味着固定了起点和终点,要想得到差的最小值当然还是需要排序,但取数就不能是一个一个取了。
排序后起点和终点一般情况就不再是在两端,而是在中间。由于我们要的是差的绝对值,所以我们以 s[ 0 ] 做起点还是以 s[ n ] 做起点都是可以的!
我们设 min{ s[ 0 ],s[ n ] } 做起点,max{ s[ 0 ],s[ n ] }做终点;还需要注意的是特殊情况,即 s[ 0 ] == s[ n ] 时,我们须确保起点的下标要小于终点的下标,不然会发生取数产生重叠区。
如上图所示,取数以起点开始到最小值,再到最大值,最后到达终点。
还有一个问题,那就是上图中存在两段重叠区。重叠区取数也必须要保证向左时和向右时差的绝对值的最大值最小!
隔一个数取一个数显然是最好的解决办法了,向左取数时候就留下了间隔一个数的一组数,留下之后反方向向右取的数。
取数完成后遍历一遍找到差绝对值的最大值,输出。
结束!
#include <algorithm>
#include <cstring>
#include <iostream>
#include <limits.h>
using namespace std;
typedef long long LL;
const int N = 300010;
int n;
LL sum[N], a[N], s0, sn;
bool st[N];
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
scanf("%d", &n);
sum[0] = 0;
for (int i = 1; i <= n; i++)
{
scanf("%lld", &sum[i]);
sum[i] += sum[i - 1];
}
s0 = sum[0], sn = sum[n];
if (s0 > sn)
swap(s0, sn);
sort(sum, sum + n + 1);
for (int i = 0; i <= n; i++)
if (s0 == sum[i])
{
s0 = i;
break;
}
for (int i = n; i >= 0; i--)
if (sn == sum[i])
{
sn = i;
break;
}
memset(st, 0, sizeof st);
int l = 0, r = n;
for (int i = s0; i >= 0; i -= 2)
{
a[l++] = sum[i];
st[i] = true;
}
for (int i = sn; i <= n; i += 2)
{
a[r--] = sum[i];
st[i] = true;
}
for (int i = 0; i <= n; i++)
if (!st[i])
{
a[l++] = sum[i];
}
LL res = 0;
for (int i = 1; i <= n; i++)
res = max(res, abs(a[i] - a[i - 1]));
printf("%d\n", res);
}
return 0;
}