• HDU 1024 Max Sum Plus Plus --- dp+滚动数组


      HDU 1024

      题目大意:给定m和n以及n个数,求n个数的m个连续子系列的最大值,要求子序列不想交。

      解题思路:<1>动态规划,定义状态dp[i][j]表示序列前j个数的i段子序列的值,其中第i个子序列包括a[j],

           则max(dp[m][k]),m<=k<=n 即为所求的结果

           <2>初始状态: dp[i][0] = 0, dp[0][j] = 0;

           <3>状态转移:

              决策:a[j]自己成为一个子段,还是接在前面一个子段的后面

              方程:

                  a[j]直接接在前面的子段之后, 则dp[i][j] = dp[i][j-1] + a[j];

                  a[j]自己作为一个子段,先求得前面k个数组成的最大i-1段子段和,再加上a[j],

                  即dp[i][j] = max(dp[i-1][k]) + a[j],其中 1=<k<=j-1;

              即 dp[i][j] = max(dp[i][j-1] + a[j], max(dp[i-1][k]) + a[j]), 其中k>=1&&k<=j-1;

           <4>空间复杂度优化:

              本题由于n可以达到100 0000,开不出dp[n][n],因此需要利用滚动数组优化空间。

              从状态转移方程中可以看到,当前行位置为j的状态dp[i][j]的更新只需要利用到上一行的前j-1个状态最大值

              因此最多只需要两行数组

           <5>时间复杂度优化:

              每次更新dp[i][j]时,都需要找到上一行的前j-1个状态的最大值,这样复杂会达到O(n^3);

              其实,可以每次更新当前行的dp[i][j]后,将其相应的前j个数的最大值保存在pre[j]中,

              这样,在下次更新下一行时可以可以直接使用当前行保存的pre[j],而不用再搜索,

              因此,时间复杂度可以优化到O(n^2)

    /* HDU 1024 Max Sum Plus Plus --- dp+滚动数组 */
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int maxn = 1000005;
    int dp[maxn], pre[maxn];    //pre[j] = dp[i-1][
    int a[maxn];
    int m, n;
    
    int main()
    {
    #ifdef _LOCAL
        freopen("D:\input.txt", "r", stdin);
    #endif
    
        while (scanf("%d%d", &m, &n) == 2){
            //输入
            for (int i = 1; i <= n; ++i){
                scanf("%d", a + i);
            }//for(i)
            int MAX;
    
            //定义第一行的第一个状态的前驱,此时的i相当于1,dp[1][0]
            dp[0] = 0; 
            //初始化pre数组全为0,此时pre数组所对应的行应该为0,
            memset(pre, 0, sizeof pre);
            for (int i = 1; i <= m; ++i){
                //内循环做的是更新当前第i行的状态, 注意 j >= i 才有意义
                
                MAX = -10e8;    //将MAX初始化为一个很小的数
                for (int j = i; j <= n; ++j){
                    /*
                        状态转移方程里的pre[j]保存的是上一行的前j个数的最大值,
                        即pre[j] = max(dp[i-1][k]), 1<=k<=j
    
                        若a[j]直接属于第i段, 则 dp[i][j] = dp[i][j-1] + a[j]
                        在这里就是 dp[j] = dp[j-1]+a[j]
                        若a[j]自成一段,则需要找出 dp[i-1][k]的最大值,其中 1<=k<=j-1,即pre[j-1]            
                    */
    
                    dp[j] = max(dp[j - 1], pre[j - 1]) + a[j];
                    /*
                        下面pre数组和上面有细微的区别
                        上面的pre[j-1]保存是上一行(第i-1行)的前j-1个数的最小值
                        而下面的pre[j-1]更新后保存的是当前行的前j-1个数的最大值,
                        更新后是用于下一行的dp的更新,即i要+1后才会用到
                        另注意MAX是上次内循环所更新的值,即当前行(i)的前j-1个数的最大值
                    */
                    pre[j - 1] = MAX;//更新pre,保存当前行的前j-1个数的最大值,为下一行的dp更新做准备
                    if (dp[j] > MAX){
                        //这里的MAX记录的是当前行的前j位的最大值
                        //到下一个内循环j会+1,这就是为什么MAX可以在上面用于更新pre[j-1]
                        MAX = dp[j];    //MAX记录当前行的前j位的最大值
                    }
                }
            }//for(i)
            printf("%d
    ", MAX);
    
        }
    
        return 0;
    }
    View Code

              

              

  • 相关阅读:
    洛谷-P1855 榨取kkksc03
    Error: ORA-06502: PL/SQL: 数字或值错误 : character string buffer too small(触发器中使用系统动态视图导致)
    jwt ctf
    apktool+nuclei mobile
    Subdomain Takeover via Fastly ( Steps )
    乱七八糟
    Recon
    推荐几个我感觉不错的tips
    子域名收集
    清除Cookie的数据
  • 原文地址:https://www.cnblogs.com/tommychok/p/5348404.html
Copyright © 2020-2023  润新知