B - Array Walk
题意: 给定一个长度为 n 的数组,从位置1开始可以向右累加 k 次,且可以向左累加 z 次,但不能连续两次及以上向左累加,求最大累加和。
思路: 贪心的考虑,在往右累加的过程中,若需要循环节的存在,则一定只要最大的一个循环节,所以我们在枚举最终停止位置时,同时记录最大的循环节,然后计算停止在每个位置能得到的最大累加和,再取最大即可。
Code1:
#include<bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef long long LL;
const int N = 3e5+7;
LL a[N];
int main()
{
IO;
int T; cin>>T;
while(T--){
int n,k,z;
cin>>n>>k>>z;
for(int i=1;i<=n;i++) cin>>a[i];
LL ans=0,mx=0,sum=0;
k+=1;
for(int i=1;i<=k;i++){
sum += a[i];
if(i<n) mx = max(mx,a[i]+a[i+1]); //寻找最大循环节
ans = max(ans,min((k-i)/2,z)*mx+sum); //枚举右边的最终位置
}
cout<<ans<<"\n";
}
return 0;
}
思路: DP暴力求解, d p [ i ] [ j ] dp[i][j] dp[i][j] 表示第 i 位向左累加了 j 次能获得的最大累加值。
Code2:
#include<bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef long long LL;
const int N = 1e5+7;
LL a[N];
LL dp[N][6];
int main()
{
IO;
int T; cin>>T;
while(T--){
int n,k,z;
cin>>n>>k>>z;
for(int i=1;i<=n;i++){
cin>>a[i];
dp[i][0] = dp[i-1][0]+a[i]; //初始化边界
for(int j=1;j<=z;j++) dp[i][j]=0;
}
LL ans = dp[k+1][0];
for(int j=1;j<=z;j++){
for(int i=1;i<=n;i++){
if(j*2+i<=k+1){ //满足转移条件
dp[i][j] = max(dp[i][j],dp[i-1][j]+a[i]); //从i-1转移来
dp[i][j] = max(dp[i][j],dp[i+1][j-1]+a[i]); //从j-1转移来
}
ans = max(ans,dp[i][j]);
}
}
cout<<ans<<"\n";
}
return 0;
}