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


    原题传送门

    思路


    本题难度本为入门难度,因为他的数据很小,用O(n2)的暴力算法就可以AC,但是,作为一个对于认为自己水题过多而感到羞愧的OIer,我决定用O(n)的算法来做这道题.

    我不打算用前缀和(其实就是不会),用前缀和也可以做出O(n)的时间复杂度,但是很复杂,而且常数因子还比我接下来要介绍的算法的常数因子大,所以,我自认为我的这个算法是洛谷全站最好的(我翻了题解).

    思想:贪心&动态规划

    复杂度:时间复杂度O(n);空间复杂度O(n);

    如果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]

    另外,若a[1]~a[m]就是最小的范围或者n=m,则程序会出现错误,需要特判:

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

    剩下的就很简单了,直接上代码.

    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),但因为我懒,所以有兴趣的可以自行改进.

  • 相关阅读:
    HERO 3
    office的一些应用,
    网页之间的参数传弟
    一个好的数码网站
    C++遍历中删除std::hash_map元素问题
    【转】Asio与shared_ptr的一些注意事项
    delphi的字节对齐
    paypal的即时付款通知参数列表(PDT)
    vs2010下libevent的使用
    mysql 数据库 left join,right join, inner join 知识
  • 原文地址:https://www.cnblogs.com/gongdakai/p/11031577.html
Copyright © 2020-2023  润新知