【動態規劃】愛與愁的心痛

原題傳送門ios

思路


本題難度本爲入門難度,由於他的數據很小,用O(n2)的暴力算法就能夠AC,可是,做爲一個對於認爲本身水題過多而感到羞愧的OIer,我決定用O(n)的算法來作這道題.算法

我不打算用前綴和(其實就是不會),用前綴和也能夠作出O(n)的時間複雜度,可是很複雜,並且常數因子還比我接下來要介紹的算法的常數因子大,因此,我自認爲個人這個算法是洛谷全站最好的(我翻了題解).數組

思想:貪心&動態規劃性能

複雜度:時間複雜度O(n);空間複雜度O(n);spa

若是a[n]<a[m+1],那麼a[n]~a[m]區間的和必定小於a[n+1]~a[m+1]區間的和.
而a[n+1]~a[m+1]區間的和減a[n]~a[m]區間的和就等於a[m+1]-a[n].
則用dp[m+1]表示a[m+1]-a[n],那麼a[n+2]~a[m+2]區間的和減a[n]~a[m]區間的和就等於a[m+1]-a[n]+a[m+2]-a[n+1]也就是b[m+1]+a[m+2]-a[n+1].而他的值又能夠用dp[m+2]表示
因此獲得dp[m+1]到dp[m+2]的]狀態轉移方程爲:dp[m+2]=dp[m+1]+a[m+2]-a[n+1].
因此dp[m+i-1]到dp[m+i]的狀態轉移方程爲dp[i+m]=dp[i+m-1]+a[i+m]-a[i]code

另外,若a[1]~a[m]就是最小的範圍或者n=m,則程序會出現錯誤,須要特判:遞歸

if(dp[no]>0||n==m)//特判
        no=m;

剩下的就很簡單了,直接上代碼.ci

Code


#include <iostream>
#include <queue>
using namespace std;

long long n,m,gkmin=99999999,ans,no;//我就是喜歡把gkmin設的大一點,你咬我啊
long long a[3002];
long long dp[3002];

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    for(int i=1;i<=n-m;i++)
    {
        dp[i+m]=dp[i+m-1]+a[i+m]-a[i];//狀態轉移
        if(gkmin>dp[i+m])
            gkmin=dp[i+m],no=i+m;
    }
    if(dp[no]>0||n==m)//特判
        no=m;
    for(int i=no;i>no-m;i--)
        ans+=a[i];
    cout<<ans;
    return 0;
}

後記

爲了裝逼敲一個高性能代碼,我足足提交了20次左右,才AC.好吧,我認可是我本身在折磨我本身QAQ.
另外,此算法仍存在改進空間,能夠用遞歸的迭代法代替動態規劃的龐大dp數組,從而將空間複雜度降爲O(1),但由於我懶,因此有興趣的能夠自行改進.get

相關文章
相關標籤/搜索