POJ-1050

一道颇为经典的DP题。

首先,先看一个比该问题更简单更基础的问题:最大子序列和,或者叫最大子串和,连续子序列最大和等等。

问题很简答:给出一个一维数组A,求它的一个子数组,使得数组元素的和最大。

比如:给定数组A{5,-3,4,2},那么它的最大子序列为{5,-3,4,2},和为8,而{5,-6,4,2}的最大子序列是{4,2},和为6.仔细看这两个列子,我们会发现,找最大子序列的方法其实很简单:遍历原数组,假设当前扫描到第 i 个元素a ,假设以第 i-1 个元素为结尾的子序列和最大值b大于0,那么我们可以继续向后扫描,累加元素。反之,如果b小于0,那么如果把前面的串继续向后扩展,得到的和会比直接从a开始的子串小,所以应该把前面的串舍弃。同时,我们还应该记下每次算出的子序列和,如果比当前最大和大,则更新它。因为有可能出现所有元素都为负数的情况,所以最大和应该初始化为元素值的下限而不是0。

实例:

data:       1  -2  3  10  -4  7    2   -5

b:    0    1  -1  3  13  9   16  18  13

max:  -127  1   1     3  13  13   16  18  18

有了上面这个复杂度为O(n)的求一维数组最大子序列和的算法后,最大子矩阵和的问题就可以转化为这个问题从而得到解决了。首先是如何进行转化:一个矩阵的元素和,等于该矩阵中每一列的元素和再求和(相当于先把矩阵纵向压成一个数组,再把这个数组横向压成一个总和)。假设用c[i][j]表示方阵第 j 列前 i 行的元素和,那么c[i1][j] - c[i2 - 1][j]就可以表示第 j 列从第 i2 行到第 i1 行的元素之和。这样,对于每个行号 i1 和 i2,我们可以计算出每列在这两行之间的列元素和,这样就构成了一个一维数组,再对这个数组用上述最大子序列和算法,就可以得到由i2、i1确定上下边界的所有子阵的最大和了。由此,只需对所有的 i2<=i1 进行上述计算,记录找到的最大值即可。复杂度O(n^3)。

AC代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define N 1005
int a[N][N];
int c[N][N];
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        int i,j,k;
        for(i=0;i<n;i++)
        {
            for(j=0;j<n;j++)
            {
                scanf("%d",&a[i][j]);
            }
        }
        memset(c,0,sizeof(c));
        for(i=1;i<=n;i++)
        {
            for(j=0;j<n;j++)
            {
                c[i][j]=c[i-1][j]+a[i-1][j];
            }
        }
        int t,sum=0;
        int M=0;
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=i;j++)
            {
                sum=0;
                for(k=0;k<n;k++)
                {
                    t=c[i][k]-c[j][k];
                    if(sum>0)
                    {
                        sum+=t;
                    }
                    else
                    {
                        sum=t;
                    }
                    M=max(sum,M);
                }
            }
        }
        printf("%d\n",M);
    }
    return 0;
}


你可能感兴趣的:(POJ-1050)