震惊!!天天刷dp的人竟没有过B!!
呜呜,wa了10几发,结果是边界的脑残错误
Ⅰ.分析
这是个很明显的dp,无后效性
原因是我们可以把在位置i往左走了q次的最大价值都记录下来
后续可以从任意一个位置转移
Ⅱ.两种DP方式
Ⅰ.好理解一点的,也是我用的方式
定义dp[i][q]是当前在索引i位置且往左走了q次的最大价值
一般我们从i走到i−1都要再走回i(因为不能连续往左走)
所以特判一下终点是往左走但次数k用完不回来的情况
一般情况当(i−1+q∗2)==k时就可以用dp数组更新答案
所以在i点时,直接枚举在i−1和i间来回走几次即可
for(int i=2;i<=n;i++)
{
dp[i][0]=dp[i-1][0]+a[i];//一直往右走
if( i-1==k ) ans=max(ans,dp[i][0]);
for(int q=1;q<=z;q++)//循环当前向左走了q次
for(int j=0;j<=q;j++)//循环
{
int s=j*(a[i]+a[i-1]);
dp[i][q]=max(dp[i][q],dp[i-1][q-j]+s+a[i]);
if( i-1+q*2==k ) ans=max(ans,dp[i][q]);
if( i-1+q*2-1==k ) ans=max(ans,dp[i][q]-a[i]);
//这里减去a[i],代表执行i->i-1->i这样操作(j-1)次,但最后一次只执行i->i-1,不回来了
}
}
完整代码戳我啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊
Ⅱ.另一种方式
状态还是相同,转移大同小异
不再枚举在第i个位置和i−1个位置间循环移动几次
直接转移dp[i][j]=dp[i−1][j]+a[i]//i−1位置往右走
dp[i−1][j+1]=max(dp[i−1][j+1],dp[i][j]+a[i−1])//i位置往左走
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int long long
int t,k,z,a[100009],dp[100009][6],n;
signed main()
{
cin >> t;
while( t-- )
{
int ans=0;
cin >> n >> k >> z;
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
dp[1][0]=a[1];
for(int i=2;i<=n;i++)
for(int j=0;j<=z;j++)
{
dp[i][j]=dp[i-1][j]+a[i];//直接从i-1走过来
if( i-1+j*2==k ) ans=max(ans,dp[i][j]);
if( j+1<=z )
dp[i-1][j+1]=max(dp[i-1][j+1],dp[i][j]+a[i-1]);//也可以从i走回i-1
if( (i-1)-1+(j+1)*2==k ) ans=max(ans,dp[i-1][j+1]);
}
for(int i=0;i<=n;i++)
for(int j=0;j<=z;j++)
dp[i][j]=0;
cout << ans << endl;
}
}
一起努力加油吧~