• HDU 1024 MAX sum plus plus 动态规划


    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1024

     

    Max Sum Plus Plus

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
    Total Submission(s): 12181    Accepted Submission(s): 4024


    Problem Description
    Now I think you have got an AC in Ignatius.L's "Max Sum" problem. To be a brave ACMer, we always challenge ourselves to more difficult problems. Now you are faced with a more difficult problem.

    Given a consecutive number sequence S1, S2, S3, S4 ... Sx, ... Sn (1 ≤ x ≤ n ≤ 1,000,000, -32768 ≤ Sx ≤ 32767). We define a function sum(i, j) = Si + ... + Sj (1 ≤ i ≤ j ≤ n).

    Now given an integer m (m > 0), your task is to find m pairs of i and j which make sum(i1, j1) + sum(i2, j2) + sum(i3, j3) + ... + sum(im, jm) maximal (ix ≤ iy ≤ jx or ix ≤ jy ≤ jx is not allowed).

    But I`m lazy, I don't want to write a special-judge module, so you don't have to output m pairs of i and j, just output the maximal summation of sum(ix, jx)(1 ≤ x ≤ m) instead. ^_^
     
    Input
    Each test case will begin with two integers m and n, followed by n integers S1, S2, S3 ... Sn.
    Process to the end of file.
     
    Output
    Output the maximal summation described above in one line.
     
    Sample Input

    1 3 1 2 3 

    2 6 -1 4 -2 3 -2 3

     
    Sample Output

    6

    8

     

    =======================================================================================

    =====================================我是分割线=========================================

    =======================================================================================

     

    这题的思路是看了别人的博客,好久才想明白!

    博客 地址:http://blog.sina.com.cn/s/blog_677a3eb30100jxqa.html

    思路:
    dp[i][j]表示前j个元素分成i段的最优解,同时这个最优解是由a[j]元素结束的。
    转移方程是dp[i][j] = max { f[i][j-1]+a[j],max(f[i-1][k]+a[j],  (i-1<=k<j)  } (i<=j<=n-m+i)
    其中j的上下界的确定比较麻烦。现在分别解释上界和下界:
    上界:dp[i][j]中,如果j=i-1,意思就是在前面i-1个元素中分成i段,这个是不可能实现的。
    下界:如果m=n=4,这时dp[2][4]求出来了,意思是前面的四个元素分成了两段,当是还有两段要分,
               所以求出这个是没有意义的。当然求出来也不会影响结果,只是这样时间复杂度就提高了。

    这是其中一个特例的状态转移表 m=4,n=6 ,-1 4 -2 3 -2 3

    图 1​

     

     

    没有填的说明不用算。
    很显然dp[i][i]=dp[i-1][i-1]+a[i],
    所以对角线上面有:
     
    图 2

     

    现在演示一下转移过程。如何求下图的框中的元素的值

     

    图 3


     

    由下面涂色的元素的最大值加上a[3]=-2求的如下图

     

    图 4

     


     

     

    最大的是4,所以4+(-2)=2;框中填2;
    假如框中的元素是dp[i][j],画圈的元素表示的是,左边那个是dp[i][j-1],上面的几个是dp[i-1][k](i-1<=k<j)} ,这个就是上面的转移方程的表格表示法。

     

    以上是大牛做的解说介,下面是我自己的看法:

    我们可以这么理解,把这n个数求最大的 m个子串 和, 那么我们可以把最后的 m 个数分离出来,这最后的m个数肯定需要加入计算范围,题目要求子串不能互相包含,则可以把m个数依次加入计算范围,加入第 i 个数就计算了 i 个子串和,直到 i == m,这样我们就减少了计算,节约了时间开销!

    比如测试数据的第二组:

                   a[1]     a[2]      a[3]    a[4]     a[5]     a[6]

    2 6     -1    4    -2    3    -2    3    中间的是dp状态

    i = 0    0    0     0    0     0    0    

    i = 1   -1    4     2    5     3    6    这里的dp[1][6]其实可以不用计算,这里只是为了方便说明

    i = 2   -1    3     2    7     5    8    

     

    i == 0 : 初始状态

    i == 1 : 保存的是到a[j]为止的一段子串和(根据的是上一状态dp[i-1][0]到dp[i-1][j-1]中的最大值 + a[j]与dp[i][j-1] + a[j]两者中 的最大值)

    i == 2 : 保存的是到a[j]为止的两段子串和(根据同上)

     

    解题代码:

     

    G++代码
     1 // File Name: Max Sum Plus Plus 1024.cpp
     2 // Author: sheng
     3 // Created Time: 2013年04月30日 星期二 16时46分15秒
     4 
     5 #include <stdio.h>
     6 #include <string.h>
     7 #include <iostream>
     8 using namespace  std;
     9 #define Max(a, b) (a) > (b) ? (a) : (b)
    10 typedef __int64 LL;
    11 
    12 const int max_n = 1e6 + 5;
    13 LL s[max_n];
    14 LL dp[2][max_n];//由于矩阵可能很大所以采用滚动数组比较实用
    15 
    16 int main()
    17 {
    18     int n, m;
    19     int i, j;
    20     while (~scanf ("%d%d", &m, &n))
    21     {
    22         for (i = 1; i <= n; i ++)
    23         {
    24             scanf ("%I64d", &s[i]);
    25             dp[0][i] = dp[1][i] = 0;//初始化dp数组
    26         }
    27         int pos = 1;     //初始使用的滚动数组中的一行
    28         for (i = 1; i <= m; i ++)     //这里从一段子串和开始直到m段子串和
    29         {    // 1
    30             dp[pos][i] = dp[pos^1][i-1] + s[i];
    31             LL max = dp[pos^1][i-1];
    32             for (j = i + 1; j <= n - m + i; j ++)
    33             {
    34                 max = Max ( max, dp[pos^1][j-1] );
    35                 dp[pos][j] = Max ( max + s[j], dp[pos][j-1] + s[j]);
    36             }
    37              // 2
    38             ///////1~2已经在思路中说明就不多说了
    39             pos ^= 1; //滚动数组实现滚动的条件
    40         }
    41         pos ^= 1;  //当i > m 时要记得返回上一状态
    42         LL ans = -1e10;
    43         for (i = m; i <= n; i++) //这个循环为了找到最大的 m段子串和
    44         {
    45             ans = Max (ans, dp[pos][i]);
    46         }
    47         printf ("%I64d\n", ans);
    48     }
    49     return 0;
    50 }

     

  • 相关阅读:
    CTF-域渗透--SSH服务
    CTF-域渗透--SMB信息泄露
    CTF-域渗透--FTP服务后门利用
    Codeforces Round #628 (Div. 2)
    模板
    模板
    Educational Codeforces Round 6
    Educational Codeforces Round 5
    模板
    Codeforces Round #627 (Div. 3)
  • 原文地址:https://www.cnblogs.com/shengshouzhaixing/p/3053563.html
Copyright © 2020-2023  润新知