zoukankan      html  css  js  c++  java
  • 最大子段和问题及其推广

    实在是太颓废了,所以开始写点东西吧。。。。

    最大子段和: 给定N个数(可能为负整数)组成的序列a1...an,求该序列形如∑ak(i<=k<=j)的子段和的最大值。当所有整数均为负整数时定义其最大的子段和为0.

    依据题意我们可以确定 最优值为max(0,max∑ak(i<=i<=j<=n)); 若我们令b[j] = max∑ak(1<=i<=j)是从序列a的第i个数到第j个数的最大和,那么我们最后的答案就是找出b数组里面记录的最大的那个值就是答案。由b[j]的定义可以知道b[j-1]>0时,b[j] = b[j-1]+a[j]否则b[j] = a[j];由此得出递推式b[j] = max(b[j-1]+a[j],a[j]) (1<=j<=n)

    代码如下

     1 int Maxsum1(int n,int *a)
     2 {
     3     int sum = 0,b = 0;
     4     for(int i=1;i<=n;i++){
     5         if(b>0) b += a[i];
     6         else b = a[i];
     7         if(b > sum ) sum = b;
     8     }
     9     return  sum;
    10 }
    View Code

    最大子段和的推广

    (1)最大子矩阵:给定一个m行n列的整数矩阵A,求其元素和最大的子矩阵

    很明显最大子矩阵和的问题是最大子段和的问题向二维的推广,我们用数组a[1:m][1:n]表示给定的矩阵A 其子数组a[i1:i2][j1:j2]表示左上角和右下角的行列坐标分别为(i1,j1)(i2,j2)的子矩阵 其个各个元素之和 记为 S(i1,i2,j1,j2)= ∑∑a[i][j] (i1<=i<=i2,j1<=j<=j2) 那么ans = Max S(i1,i2,j1,j2) (1<=i1<=i2<=m,1<=j1<=j2<=n)

    我们可以令t(i1,i2) = maxS(i1,i2,j1,j2)(i<=j1<=j2<=n) = Max∑∑a[i][j] 同样我们令b[j] = ∑a[i][j](i1<=i<=i2) 于是 t(i1,i2) = max∑b[j](1<=j1<=j2<=n) 这又回到了一维的情况于是我们可以得到如下代码

     1 int Maxsum2(int m,int n,int a[105][105] )
     2 {
     3     int sum = 0;
     4     int b[105];
     5     for(int i=1;i<=m;i++){
     6         memset(b,0,sizeof(b));
     7         for(int j=i;j<=m;j++){
     8             for(int k=1;k<=n;k++) b[k] += a[j][k];
     9             int Max = Maxsum1(n,b);
    10             if(Max > sum) sum = Max;
    11         }
    12     }
    13     return sum ;
    14 }
    View Code

    最大子矩阵在poj上面有原题目 题号是1050 代码如下

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    
    using namespace std;
    
    const int M = 105;
    
    int a[M][M];
    
    int Maxsum1(int n,int *a)
    {
        int sum = 0,b = 0;
        for(int i=1;i<=n;i++){
            if(b>0) b += a[i];
            else b = a[i];
            if(b > sum ) sum = b;
        }
        return  sum;
    }
    
    int Maxsum2(int m,int n,int a[105][105] )
    {
        int sum = 0;
        int b[105];
        for(int i=1;i<=m;i++){
            memset(b,0,sizeof(b));
            for(int j=i;j<=m;j++){
                for(int k=1;k<=n;k++) b[k] += a[j][k];
                int Max = Maxsum1(n,b);
                if(Max > sum) sum = Max;
            }
        }
        return sum ;
    }
    
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++)
                scanf("%d",&a[i][j]);
        }
        printf("%d
    ",Maxsum2(n,n,a));
    
    
    
    
        return 0;
    }
    View Code

     2 最大M子段和

    最大子段和是最大M子段和的特殊情况即M = 1时的解,我们用b(i,j)表示数组a的前j项中i个子段和的最大值 那么很容易得到如下状态转移方程 b(i,j) = max(b(i,j-1),max(b(i-1,t))+a[j] 第一个状态表示第i个子段包含a[j],而第二个状态表示第i个子段只包含a[j] 于是很容易得到如下代码

     1 int MaxSum()
     2 {
     3     memset(b,0,sizeof(b));
     4     for(int i=1;i<=m;i++){
     5         for(int j=i;j<=n-m+i;j++){
     6             if(j>i){
     7                 b[i][j] = b[i][j-1]+a[j];
     8                 for(int k=i-1;k<j;k++){
     9                     b[i][j] = max(b[i][j],b[i-1][k]+a[j]);
    10                 }
    11 
    12             }
    13             else
    14                 b[i][j] = b[i-1][j-1] + a[j];
    15 
    16         }
    17     }
    18     int sum = 0;
    19     for(int j=m;j<=n;j++){
    20         if(sum < b[m][j]) sum = b[m][j];
    21     }
    22     return sum ;
    23 }
    View Code

    上述算法显然需要O(mn^2)但是我们注意到 对b[i][j] 只用到了 第i-1行和第i行因此只要记录这两个值就可以,另一方面max(b(i-1,t))的值可以在计算第i-1行是预先计算出来

    于是得到优化后的代码如下

    int dp()
    {
    
    
        b[0] = 0;
        c[1] = 0;
    
        for(int i=1;i<=m;i++){
            b[i] = b[i-1] + a[i];
            c[i-1] = b[i];
            int tmp = b[i];
            for(int j=i+1;j<=n-m+i;j++){
                b[j] = max(b[j-1],c[j-1])+a[j];
                c[j-1] = tmp;
                if(tmp < b[j]) tmp = b[j];
    
            }
    
            c[i+n-m] = tmp;
    
        }
        printf("%d
    ",c[n]);
    
    }
    View Code

    最大M子段和问题在杭电OJ上有原题 题号是1024 

    AC代码如下

    #include <cstdio>
    #include <iostream>
    #include <cstring>
    
    using namespace std;
    
    const int M = 1000005;
    
    int n,m;
    int a[M];
    int b[M],c[M];
    
    
    int dp()
    {
    
    
        b[0] = 0;
        c[1] = 0;
    
        for(int i=1;i<=m;i++){
            b[i] = b[i-1] + a[i];
            c[i-1] = b[i];
            int tmp = b[i];
            for(int j=i+1;j<=n-m+i;j++){
                b[j] = max(b[j-1],c[j-1])+a[j];
                c[j-1] = tmp;
                if(tmp < b[j]) tmp = b[j];
    
            }
    
            c[i+n-m] = tmp;
    
        }
        printf("%d
    ",c[n]);
    
    }
    int main ()
    {
        while(scanf("%d%d",&m,&n)!=EOF){
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
                dp();
                memset(c,0,sizeof(c));
        }
    
    
        return 0;
    }
    View Code
    It is loneliness that make you different,not gregariousness
  • 相关阅读:
    批处理读取INI文件
    重装操作系统的20条原则
    SATA串口硬盘Windows Vista系统驱动安装实录
    中国国家地理高清晰的PDF书籍系列经典珍藏版
    单一职责原则
    理解boost::bind的实参传递方式
    理解C++ dynamic_cast
    C# vs C++之三:静态构造函数
    TDD可以驱动设计吗?
    依赖注入与对象间关系
  • 原文地址:https://www.cnblogs.com/lmlyzxiao/p/4640901.html
Copyright © 2011-2022 走看看