总提交 : 69 测试通过 : 2
比赛描述
BJ梦到自己被n个妹子围住,每个妹子对他有个好感度ai,他对这些妹子的吸引力被定义为位置连续的妹子对他好感度和的最大值,现在他想知道自己的吸引力是多少。
输入
第一行为一个数字T代表数据组数,每组数据的第一行为一个数字n表示妹子个数,下一行包含n个数,代表第i个妹子对他的好感度。
(1 <= T <= 5,1 <= n <= 2*106,|ai| <= 108,1 <= i <= n)
输出
每组数据输出一个数,代表BJ对该组妹子的吸引力。
样例输入
3
1
100000000
2
-1 1000
7
-1 1 -2 2 3 -1 5
样例输出
100000000
1000
9
提示
因为他被围住,所以a1和an位置上是相邻的。
#include<stdio.h>
int main()
{
int T,i,n,x,y,z;
int before,sum;
int static a[2000001];
int max1=0,max2=0,max;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
if(n==1)
{
scanf("%d",&a[0]);
printf("%d\n",a[0]);
a[0]=0;
}
else
{
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
a[n]=a[0];
for(i=1;i<n;i++)
{
if(i==1)
{
sum=before=a[1];
x=y=z=i;
}
else
{
if(a[i]+before>a[i])
before=before+a[i];
else
{
before=a[i];
x=i;
}
a[i]=0;
}
if(before>sum)
{
sum=before;
y=x;
z=i;
}
}
for(i=0;i<y;i++)
max1+=a[i];
for(i=z+1;i<=n;i++)
max2+=a[i];
if(sum+max1>sum)
max1=sum+max1;
if(sum+max2>sum)
max2=sum+max2;
max=(max1>=max2)?max1:max2;
if(sum<max)
sum=max;
a[n]=0;
a[0]=0;
printf("%d\n",sum);
}
}
return 0;
}
没看懂这份代码的意思(没缩进没注释我尽力了
这个就是个环形最大子段和问题。先读入a[1]...a[n],然后复制一份在后面a[n+1]...a[2n]。
然后现在问题就变成了在这个长度为2n的区间内找一段长度不超过n的子区间要求其区间和最大。
对这个序列求前缀和sum,问题就变成了对于一个sum[i],找一个sum[j](1<=i-j<=n),使得sum[i]-sum[j]最大。
注意到在枚举i的过程中,sum[i]是个定值,那么就要找一个sum[j](1<=i-j<=n),使得sum[j]最小。
用单调队列维护sum[j],因为对于j1,j2,设j1<j2,那么如果sum[j1]>=sum[j2],那么对于以后所有的i,j1显然不是一个决策点,这时把j1出队。(因为随着i的变大,j1会先于j2离i的距离超过n。)如果队列中有超过n个元素,也把离i的位置超过n的元素出队。时间复杂度0(n)。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using
namespace
std;
#define LL long long
const
int
MAXN = 100010;
int
n, N;
LL a[MAXN*2], ans;
int
q[MAXN*2];
int
main()
{
int
i, l = 1, r = 1, t;
scanf
(
"%d"
, &n);
for
(i = 1; i<=n; ++i) {
scanf
(
"%d"
, &t);
a[i] = a[i+n] = t;
}
N = 2*n - 1;
for
(i = 1; i<=N; ++i)
a[i] += a[i-1];
q[r++] = 0;
ans = a[1];
for
(i = 1; i<=N; ++i)
{
while
(l<r && q[l] < i-n) ++l;
ans = max(ans, a[i] - a[q[l]]);
while
(l<r && a[q[r-1]] > a[i]) --r;
q[r++] = i;
}
cout << ans <<
'\n'
;
return
0;
}
也可以用数据结构维护sum[j],相当于在区间[i-n,i-1]内找一个元素的最小值,用线段树,st表等等都可以做。时间复杂度0(nlogn)。
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using
namespace
std;
struct
node{
int
l,r;
ll minw;
}tree[800010];
ll num[200010],ans=-999999;
int
n,head,tail;
void
build(
int
x,
int
l,
int
r)
{
tree[x].l=l,tree[x].r=r;
if
(l==r)
{
tree[x].minw=num[l];
return
;
}
build(2*x,l,(l+r)/2);
build(2*x+1,(l+r)/2+1,r);
tree[x].minw=min(tree[2*x].minw,tree[2*x+1].minw);
}
ll query(
int
x,
int
l,
int
r)
{
if
(l==tree[x].l&&r==tree[x].r)
return
tree[x].minw;
if
(tree[x].l>r||r<tree[x].l)
return
100000*100000LL;
if
(r<=(tree[x].l+tree[x].r)/2)
return
query(2*x,l,r);
if
(l>(tree[x].l+tree[x].r)/2)
return
query(2*x+1,l,r);
return
min(query(2*x,l,(tree[x].l+tree[x].r)/2),query(2*x+1,(tree[x].l+tree[x].r)/2+1,r));
}
int
main()
{
scanf
(
"%d"
,&n);
for
(
int
i=1;i<=n;++i)
scanf
(
"%lld"
,&num[i]);
for
(
int
i=n+1;i<2*n;++i)
num[i]=num[i-n];
for
(
int
i=1;i<2*n;++i)
num[i]+=num[i-1];
build(1,1,2*n-1);
for
(
int
i=1;i<2*n;++i)
if
(i==1)ans=max(ans,num[i]);
else
ans=max(ans,num[i]-query(1,max(i-n,1),i-1));
printf
(
"%lld\n"
,ans);
}
注意代码仅供参考,要提交前应改成多组输入数据的版本,还有修改一下最值的界。然后这题数据比较大,答案要开long long。