B. Array Walk(两种dp方式)

震惊!!天天刷dp的人竟没有过B!!

呜呜,wa了10几发,结果是边界的脑残错误

. \color{Red}Ⅰ.分析

d p , 这是个很明显的dp,无后效性

i q 原因是我们可以把在位置i往左走了q次的最大价值都记录下来

后续可以从任意一个位置转移

. D P \color{Red}Ⅱ.两种DP方式

. , Ⅰ.好理解一点的,也是我用的方式

d p [ i ] [ q ] i q 定义dp[i][q]是当前在索引i位置且往左走了q次的最大价值

i i 1 i ( ) 一般我们从i走到i-1都要再走回i(因为不能连续往左走)

k 所以特判一下终点是往左走但次数k用完不回来的情况

( i 1 + q 2 ) = = k d p 一般情况当(i-1+q*2)==k时就可以用dp数组更新答案

i , i 1 i 所以在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 不再枚举在第i个位置和i-1个位置间循环移动几次

d p [ i ] [ j ] = d p [ i 1 ] [ j ] + a [ i ] / / i 1 直接转移dp[i][j]=dp[i-1][j]+a[i]//i-1位置往右走

d p [ i 1 ] [ j + 1 ] = m a x ( d p [ i 1 ] [ j + 1 ] , d p [ i ] [ j ] + a [ i 1 ] ) / / i 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;
	}
}

一起努力加油吧~

猜你喜欢

转载自blog.csdn.net/jziwjxjd/article/details/107680490