想了好久终于把整个过程想明白 其实这个题目的思路还是比较清楚的 只不过有几个点很难想清楚
涉及算法
1.前缀和
2.排序
3.贪心
总体思路
该题实际上要求通过何种灵能传输可以使得该序列的最大值最小
而由前缀和可知 一个有序的前缀和序列 其max(s[i]-s[i-1])的最大值可以达到最小
(关于这个点大家可以画个图理解一下)
通过对几个样例的观察可以发现一个规律
1.对于ai有
1.a[i]>0时 a[i-1]=a[i-1]+a[i] 则 s[i-1]= 原来的s[i]
a[i]=a[i]-2*a[i] 则 原s[i]= s[i-1] + a[i]
则 现s[i]= 现s[i-1] - a[i]= 原s[i]- a[i]=原s[i-1]
a[i+1]=a[i+1]+a[i] 参考上述推导 可得 s[i+1]=原s[i+1]
这意味着除了s[0]和s[n]以外1~n的任何s[i]可以进行相互交互从而得到一个有序的序列
而a[i]=s[i]-s[i-1]
也就意味着可以通过交换s[i]的方式得到灵能传输后最终结果
for(int i=1;i<=n;i++)
scanf("%d",&a[i],s[i]=s[i-1]+a[i]);
sort(s+1,s+1+n);
如果s[0],s[n]也可以正常交换的话那么这题的推导到这步就可以结束了
我们可以通过直接计算max(s[i]-s[i-1]的值 获得最大值的最小值
但问题在于 s[0],s[n]
即我们最终得到的一个序列并不一定是单调的
所以接下来我们就要通过一系列操作解决不单调序列的问题
2.通过上述分析可以明确想要求得本题的最优解应使得所求序列尽量保持单调
通过画图可知一个有两个拐点的曲线重叠部分最小时 单调部分最多
而一个曲线符合下列情况时符合要求:
1.左端点小于右端点 即要求s[0]<s[n]
//所以在记录s0和sn的值的时候 需要进行一次特判
if(s0>sn)
swap(s0,sn);
2.极小值在极大值左边
该点要求我们在后续选点的时
应s[0]向左取 s[n]向右取 因为只有这样才能取得两边的极值
int l=0,r=n-1;
//借助一个st数组 记录所取数的情况
for(int i=s0;i>=0;i-=2)
f[l++]=s[i],st[i]=true;
for(int i=sn;i<=n;i+=2)
f[r--]=s[i],st[i]=true;
for(int i=0;i<=n;i++)
if(st[i]==false)
f[l++]=s[i];
这样以后就可以保证序列为f为重叠部分最小的前缀和序列
int res=0;
for(int i=1;i<=n,i++)
res=max(res,abs(f[i]-f[i-1]));
res即为所求结果
以下为完整代码
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=3e5+10;
//由于a[]可能达到1e9所以需要使用到LL
typedef long long LL;
LL a[N]; //用于存放初始灵能值
LL s[N]; //用于存放前缀和
LL f[N]; //用于存放最终的有序序列
bool st[N];
int main()
{
int T;
scanf("%d",&T);
while (T--)
{
int n;
scanf("%d",&n);
s[0]=0;// 注意这一步不要忘了
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]),s[i]=s[i-1]+a[i];
LL s0=s[0],sn=s[n];
if(s0>sn)
swap(s0,sn);
sort(s,s+1+n);
//找到排序后 s0,sn的位置
for(int i=0;i<=n;i++)
if(s0==s[i])
{
s0=i;
break;
}
for(int i=0;i<=n;i++)
if(sn==s[i])
{
sn=i;
break;
}
memset(st,false,sizeof st);
//构造重叠部分最小的序列
int l=0,r=n;
for(int i=s0;i>=0;i-=2)
f[l++]=s[i],st[i]=true;
for(int i=sn;i<=n;i+=2)
f[r--]=s[i],st[i]=true;
for(int i=0;i<=n;i++)
if(st[i]==false)
f[l++]=s[i];
LL res=0;
for(int i=1;i<=n;i++)
res=max(res,abs(f[i]-f[i-1]));
printf("%lld\n",res);
}
return 0;
}
想问一下为什么找s0和sn那一步不可以这样写
我也有这疑惑,哥你解决没
这样的话显然是不对的,如果s0 == sn的话,会将s0,sn赋成相同的下标。可以将第二句改为
else if
,但是还应该加bool变量判断是否找到s0,sn,不然会在s0,sn更新后会再次更新为错误的下标。这样就能ac了
确实是这样,谢谢哥,我把找sn那个循环的break去掉也是对的
很清晰!!
请问一下,第一个for循环后面,排在第一个的武士不应该是s[1]吗?为什么是s[0]?
s[1]可以与s[2]进行交换,但s[0]不能参与交换
它的第一个武士下标是0
666
666
楼主,题解和代码对应不上啊
不好意思.... 那天写的时候可能是按照我自己的理解来理一遍 代码是参考yls的 现已修改