【动态规划】爱与愁的心痛

原题传送门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

相关文章
相关标签/搜索