1. 程式人生 > 其它 >題解CF1389B Array Walk

題解CF1389B Array Walk

題目連結

\(Describe\)

\(n\)個元素的陣列,陣列的值表示得分,最初從下標為\(1\)的位置開始,總共可以向左或者向右移動\(k\)次,且向左移動的總次數不能超過\(z\)次,求恰好走\(k\)次的最大得分。

\(Solution\)

看到這題的第一思路就是\(dp\),因為每次移動只能向左右移動,所以這題是一道線性\(dp\)問題,那麼接下來的問題就是如何設定引數以及如何寫狀態轉移方程了。

我們可以發現\(n<10^5\),以及\(z\)的最大值為\(5\),於是就可以用二維\(dp\)去標記狀態,\(dp[i][j]\)表示總共移動了\(i\)次、其中向左移動了\(j\)

次的最大得分,其狀態轉移方程如下

\(dp[i][j]=dp[i-1][j]+a[i-j*2+1]\)

\(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\)很小。

\(Code\)

#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;
}