谁能拿到最多的硬币

描述
有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=0ik,iZmax(j=0iaj+j=n(ki)+1naj)但是,如果对于每个 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=0ik,iZmax(psi+psnpsn(ki))让 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; 
} 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值