zoukankan      html  css  js  c++  java
  • POJ1050 To the Max

    题目来源:http://poj.org/problem?id=1050

    题目大意:

      给出一个N*N的整数方阵,求它的一个子矩阵,使得其元素之和最大。

    例如4*4的方阵A:

    0 -2 -7 0 
    9 2 -6 2 
    -4 1 -4 1 
    -1 8 0 -2 

    左上角的子阵:
    9 2 
    -4 1 
    -1 8 
    元素之和15为最大。

    输入:第一行为一个不大于100的整数N,后接N*N个整数,分别用空格、换行或空行隔开。每个整数都在范围[-127,127]之间。

    输出:子阵元素和的最大值。


    Sample Input

    4
    0 -2 -7 0 9 2 -6 2
    -4 1 -4  1 -1
    
    8  0 -2

    Sample Output

    15

    一道颇为经典的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)。

     1 //////////////////////////////////////////////////////////////////////////
     2 //        POJ1050 To the Max
     3 //        Memory: 244K        Time: 16MS
     4 //        Language: C++        Result: Accepted
     5 //////////////////////////////////////////////////////////////////////////
     6 
     7 #include <cstdio>
     8 
     9 using namespace std;
    10 
    11 int N;
    12 int data[100][100];
    13 int c[101][100];
    14 int max_sum = -127;
    15 
    16 int main(void) {
    17     scanf("%d", &N);
    18     for (int i = 0; i < N; ++i) {
    19         for (int j = 0; j < N; ++j) {
    20             scanf("%d", &data[i][j]);
    21         }
    22     }
    23 
    24     //c[i][j]表示sum(data[0][j], data[1][j].. ,data[i - 1][j]), 即第i列前j个元素之和
    25     for (int i = 1; i <= N; ++i) {
    26         for (int j = 0; j < N; ++j) {
    27             c[i][j] = c[i - 1][j] + data[i - 1][j];
    28         }
    29     }
    30     int a, b = 0;
    31     for (int i1 = 1; i1 <= N; ++i1) {
    32         for (int i2 = 1; i2 <= i1; ++i2) {
    33             b = 0;    //b为一行中以某元素为尾的子段的最大和
    34             for (int j = 0; j < N; ++j) {
    35                 a = c[i1][j] - c[i2 - 1][j];    //第j列i1-1行至i2-1行元素之和
    36                 if (b > 0) {
    37                     b += a;    //之前字段和与当前元素值之和
    38                 } else {
    39                     b = a;    //前面的子段和不大于0,舍弃
    40                 }
    41                 max_sum = b > max_sum ? b : max_sum;
    42             }
    43         }
    44     }
    45     printf("%d", max_sum);
    46     return 0;
    47 }
    View Code
  • 相关阅读:
    stm32之不定长接收
    3、列表和列表项
    2、FreeRTOS任务相关API函数
    1、FreeRTOS移植
    5、根文件系统原理
    1、c++对c语言的扩展
    4、移植三星官方内核
    3、内核的启动过程
    2、内核的配置和移植
    iOS学习笔记19-地图(一)定位CoreLocation
  • 原文地址:https://www.cnblogs.com/dengeven/p/3254011.html
Copyright © 2011-2022 走看看