描述
有n个硬币排成一排,每个硬币上用一个数字标识了它的价值。每次要你从最左边或者最右边拿出一个硬币。总共拿k次,写一个算法,使能拿到的硬币的价值和最大。
关于输入
输入包含两行,第一行为n, k;
第二行包含n个数字,表示n个硬币的价值。
1 <= k <= n <= 100000
单个硬币的价值大于0且不超过10000.
关于输出
输出可以拿到的k个硬币最大的价值和。
例子输入
6 3
5 4 3 2 1 6
例子输出
15
由于只能连续地取左边几个和右边几个,所以答案是对 [0 , k] 之间的 i ,前面 i 个数和后面 k-i 个数的和的最大值。用
a
i
a_i
ai表示第 i 个数,于是有这个方程:
a
n
s
=
max
0
⩽
i
⩽
k
,
i
∈
Z
(
∑
j
=
0
i
a
j
+
∑
j
=
n
−
(
k
−
i
)
+
1
n
a
j
)
ans=\max_{0\leqslant i\leqslant k \,,\, i\in\mathbb Z}\Biggl( \sum_{j=0}^i a_j +\sum_{j=n-(k-i)+1}^n a_j\Biggr)
ans=0⩽i⩽k,i∈Zmax(j=0∑iaj+j=n−(k−i)+1∑naj)但是,如果对于每个 i 都算一下两个求和,肯定会超时(根据许多人的经验)
这里我们用前缀和优化一下,省略掉求和的过程(CSDN上可以搜到详细解说)
用ps[i]表示前i个数的和
所以第i个数到第j个数的和等于ps[j]-ps[i-1]
这样,上面的式子可以换成:
a
n
s
=
max
0
⩽
i
⩽
k
,
i
∈
Z
(
p
s
i
+
p
s
n
−
p
s
n
−
(
k
−
i
)
)
ans=\max_{0\leqslant i\leqslant k \,,\, i\in\mathbb Z}\bigl( ps_i +ps_n-ps_{n-(k-i)}\bigr)
ans=0⩽i⩽k,i∈Zmax(psi+psn−psn−(k−i))让 i 走一遍循环,即可得出结果。
废话不说,上代码
C++
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int ps[100005];
int main(){
int n,k,ma=0;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&ps[i]);
ps[i]+=ps[i-1];//求出前缀和
}
for(int i=0;i<=k;i++)
ma=max(ma,ps[i]+ps[n]-ps[n-(k-i)]);
printf("%d",ma);
return 0;
}
C
#include<stdio.h>
#include<string.h>
int ps[100005];
int main(){
int n,k,i,ma=0;
scanf("%d%d",&n,&k);
for(i=1;i<=n;i++){
scanf("%d",&ps[i]);
ps[i]+=ps[i-1];//求出前缀和
}
for(i=0;i<=k;i++)
if(ma<ps[i]+ps[n]-ps[n-(k-i)])
ma=ps[i]+ps[n]-ps[n-(k-i)];
printf("%d",ma);
return 0;
}