zoukankan      html  css  js  c++  java
  • 最大m段子段和

    动态规划,借助矩阵可以直观的看到计算过程。
    定义二维数组dp, dp[ i ][ j ],表示前 j 项所构成 i 子段的最大和,且必须包含着第j项,即以第j项结尾
    然后是一个递推过程。
    求dp[ i ][ j ],有两种情况
    1、dp[ i ][ j ] = dp[ i ] [ j-1 ] + a[ j ] ,即把第j项融合到第 j-1 项的子段中,子段数没变
    2、dp[ i ][ j ] = dp[ i-1 ] [ t ] + a[ j ],(i-1<= t < j )
     
     
    把第 j 项作为单独的一个子段,然后找一下i-1个子段时,最大的和,然后加上a[ j ]
     
    然后比较上面两种情况,取大的。
    下面看图,红色数字为输入的序列:
     
     
    如图,要求dp[ 3 ][ 6 ],只需比较他左边的那个,和上面那一行圈起来的之中最大的数,
    再加上a[ j ] 即为dp[ 3 ][ 6 ] 的值。
    优化一下:
    1、沿着第m行的最后一个元素,往左上方向画一条线,线右上方的元素是没必要计算的
    那么dp[ i ][ j ] ,j++的时候,j的上限为 i + n - m 即可。
    还有左下角那一半矩阵,也是不用计算的,因为1个数字不可能分为2个子段
    2、每确定一个dp[ i ][ j ],只需用到本行和上一行,所以不用开维数组也可以,省内存。
    开两个一维数组,pre和dp,pre记录上一行,dp记录当前行
    3、再对上一行红圈中的数字找最大值时,若用一个循环来找,容易超时。
     
    优化方法:在每次计算dp之前,同时记录下j前面的最大元素。
     
     
    时间复杂度大致为O(m*(n-m+1)),mn-m方
    通过图片,分析情况1和2,就能发现,从左上角走到第 m 行的最后一个元素即可,找出第 m 行的最大值即为答案。
    三、例题
    1、51nod 1052
     
    1052 最大M子段和
    基准时间限制:2 秒 空间限制:131072 KB 分值: 80 难度:5级算法题
     
     

    N个整数组成的序列a[1],a[2],a[3],…,a[n],将这N个数划分为互不相交的M个子段,并且这M个子段的和是最大的。如果M
     >= N个数中正数的个数,那么输出所有正数的和。
    例如:-2 11 -4 13 -5 6 -2,分为2段,11
     -4 13一段,6一段,和为26。
     
     
    Input
    第1行:2个数N和M,中间用空格分隔。N为整数的个数,M为划分为多少段。(2 <= N , M <= 5000)
    第2 - N+1行:N个整数 (-10^9 <= a[i] <= 10^9)
    Output
    输出这个最大和
    Input示例
    7 2
    -2
    11
    -4
    13
    -5
    6
    -2
    Output示例
    26
    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<map> 
    #include <math.h>
    #include<bits/stdc++.h> 
    using namespace std;
    typedef long long ll; 
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    const int INF=0x3f3f3f3f;
    const int maxn=1e6+100;
    int n,m;
    int a[maxn];
    int dp[maxn];
    int cmax[maxn];
    int main(){
        int n,m,Max;
        cin>>n>>m;
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        for(int i=1;i<=m;i++){
            Max=-INF;
            for(int j=i;j<=n;j++){
                dp[j]=max(dp[j-1]+a[j],cmax[j-1]+a[j]);
                cmax[j-1]=Max;
                Max=max(Max,dp[j]); 
            }
        }
        printf("%d",Max); 
    }

    例题2:

    http://acm.hdu.edu.cn/showproblem.php?pid=1024

    问题描述

    现在我想你们已经了解伊格那丢了。L的“最大和”问题。为了做一个勇敢的演员,我们总是挑战自己去解决更困难的问题。现在你面临一个更困难的问题。

     

    给定一个连续的数字序列S1, S2, S3, S4…Sx,……Sn(1≤x≤n≤1,000,000,-32768≤Sx≤32767)。我们定义了一个函数sum(i, j) = Si +…+ Sj(1≤i≤j≤n)

     

    现在给定一个整数m(m > 0),你的任务是找到m对i和j,它们组成和(i1, j1) +和(i2, j2) +和(i3, j3) +…+和(im, jm)最大(ix≤iy≤jx或ix≤jy≤jx不允许)。

     

    但是我很懒,我不想写一个特殊的判断模块,所以你不必输出m对I和j,只输出和(ix, jx)(1≤x≤m)的最大和。^ _ ^

     

     

    输入

    每个测试用例将以两个整数m和n开始,然后是n个整数S1, S2, S3…Sn。

    处理到文件末尾。

     

     

    输出

    在一行中输出上面描述的最大总和。

     

     

    样例输入

    1 3 1 2 3

    2 6 -1 4 -2 3 -2 3

     

     

    样例输出

    6

    8

     

    提示

     

    建议使用大输入、scanf和动态编程。

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<map> 
    #include <math.h>
    #include<bits/stdc++.h> 
    using namespace std;
    typedef long long ll; 
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    const int INF=0x3f3f3f3f;
    const int maxn=1e6+100;
    int n,m;
    int a[maxn];
    int dp[maxn];
    int cmax[maxn];
    int main(){
        int n,m;
        int Max;
        while(~scanf("%d%d",&m,&n)){
            memset(dp,0,sizeof(dp));
            memset(cmax,0,sizeof(cmax));
            for(int i=1;i<=n;i++){
                cin>>a[i];
            }
            for(int i=1;i<=m;i++){
                Max=-INF;
                for(int j=i;j<=n;j++){
                    dp[j]=max(dp[j-1]+a[j],cmax[j-1]+a[j]);//cmax是上一步的    
                    cmax[j-1]=Max;//为i++后准备,而不是j++,,    
                    Max=max(Max,dp[j]);
                     
                }
            }
            printf("%d
    ",Max);
        }
    }
    #include <iostream>
    
    #include <cstdio>
    
    #include <cstring>
    
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 1e6+10;
    
    const int INF = 0x7fffffff;
    
    int a[maxn];
    
    int dp[maxn];
    
    int Max[maxn];//max(dp[i-1][k])就是上一组0~j-1的最大值
    
    int main(){
    
        int n,m,mmax;
    
        while(~scanf("%d%d",&m,&n)){
    
            for(int i = 1; i <= n; i++){
    
                scanf("%d",&a[i]);
    
            }
    
            memset(dp,0,sizeof(dp));
    
            memset(Max,0,sizeof(Max));//分成0组的全是0
    
            for(int i = 1; i <= m; i++){//分成i组
    
                mmax = -INF;
    
                for(int j = i; j <= n; j++){//前j个数分成i组,至少需要i个数
    
                    dp[j] = max(dp[j-1]+a[j],Max[j-1]+a[j]);
    
                    //Max[j-1]目前代表的是分成i-1组前j-1个数的最大值,a[j]单独一组组成i组
    
                    //dp[j-1]代表j-1个数分成组,第j个数a[j]放在前面i组的一组中,两种方式选取较大者
    
                    Max[j-1] = mmax;//当前考虑的是j但是mmax是上一次循环得到的,所以更新的是j-1
    
                    mmax = max(mmax,dp[j]);//更新mmax,这样下次循环同样更新的是j-1
    
                }
    
                //这样也就更新得到了分i组的Max,下次分i+1组的时候就可以使用了
    
            }
    
            printf("%d
    ",mmax);
    
        }
    
        return 0;
    
    }

    参考:

    https://blog.csdn.net/winter2121/article/details/72848482

    https://blog.csdn.net/onion___/article/details/79157809?depth_1.utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

  • 相关阅读:
    unbuntu系统下在文件中按上下左右出现错误码
    构建之法阅读笔记
    cloudera卸载
    第二阶段团队站立会议04
    第二阶段团队站立会议03
    第二阶段团队站立会议02
    第二阶段团队站立会议01
    团队测试
    cnblogs.com的用户体验
    对于每个小组的意见
  • 原文地址:https://www.cnblogs.com/lipu123/p/12347675.html
Copyright © 2011-2022 走看看