题解CF1389B Array Walk

题目链接

题意

\(n\)个元素的数组,数组的值表示得分,最初从下标为\(1\)的位置开始,总共可以向左或者向右移动\(k\)次,且向左移动的总次数不能超过\(z\)次,求恰好走\(k\)次的最大得分。

思路

看到这题的第一思路就是\(dp\),因为每次移动只能向左右移动,所以这题是一道线性\(dp\)问题,那么接下来的问题就是如何设置参数以及如何写状态转移方程了。

我们可以发现\(n<10^5\),以及\(z\)的最大值为\(5\),于是就可以用二维\(dp\)去标记状态,\(dp[i][j]\)表示总共移动了\(i\)次、其中向左移动了\(j\)次的最大得分,其状态转移方程如下

$\large{dp[i][j]=dp[i-1][j]+a[i-j*2+1]}$

\(\large{dp[i][j]=max(dp[i][j],dp[i-1][j-1]+a[i-1-(j-1)*2])}\)

当前状态下位置的最大得分和他前一个位置有关,第一个转移方程是往右走的情况,第二个转移方程是往左走的情况,\(a\)数组表示走之后到达的位置的得分,两者取最大即为当前位置的最大得分,答案输出即为\(dp[k][i],0\le i \le z\)中的最大值

时间复杂度\(O(n)\),常数\(z\)很小。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll t,n,k,z,a[100005],dp[100005][10];
signed main()
{
    ios::sync_with_stdio(false);
    cin>>t;
    while(t--)
    {
        //不需要memset也不能用,用了的话复杂度为O(tn)会TLE
        cin>>n>>k>>z;
        for(ll i=1;i<=n;++i)cin>>a[i];
        dp[0][0]=a[1];//初始化dp数组
        dp[1][0]=a[1]+a[2];
        for(ll i=2;i<=n;++i)
        {
            for(ll j=0;j<=z;++j)
            {
                dp[i][j]=dp[i-1][j]+a[i-j*2+1];
                if(j>0)dp[i][j]=max(dp[i][j],dp[i-1][j-1]+a[i-1-(j-1)*2]);
            }
        }//进行状态转移
        ll ma=-1;
        for(ll i=0;i<=z;++i)ma=max(ma,dp[k][i]);//最大值即为答案
        cout<<ma<<endl;
    }
    return 0;
}
posted @ 2022-04-04 19:57  carnation13  阅读(66)  评论(0)    收藏  举报