HDU 1081 求子矩阵和最大值

HDU 1081 To The Max

题目链接:https://acm.dingbacode.com/showproblem.php?pid=1081

题目描述

题意:给定N * N的矩阵,每个位置都有一个[-128, 127]的数字,求一个子矩阵,满足子矩阵中数字和最大,并输出这个最大值。

样例

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

输出:

15

算法:

这题可以用DP做,也可以用贪心思想。这里使用贪心作为解。

首先我们要知道,对于一维数组而言,如何求出最大它的子段最大和?

我们只需要O(n)地扫描一遍该数列,不断把新的值加入子段,当子段和小于0时,要把当前字段和清空(再加入数字,明显与0相加更优),不断在扫描中更新最大值即可。

那么,对于二维矩阵,如何求出最大子矩阵?

我们可以枚举每个i ~ j行,把该i ~ j行的数字和看作一个"点",这样就可以用一维的方法求出在行数限定为i~j时,求出的最大矩阵和,由于枚举了所有i ~ j的情况,答案不会更优,因此求的的答案为最优解。

复杂度计算:枚举每个i ~ j行的状态,在每个状态下,需要计算每一列中i ~ j行的和,矩阵是N * N的,因此复杂度为O(n3),n最大为100,共需要计算106次,可以接受。

唯一的坑点就是有多组输入,而题目没有说明。

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

const int N = 105;

int n; // n*n矩阵
int w[N][N];
int t[N], ans; // t存储第i~j行的sum,ans存储最优解

int main () {
    #ifdef LOCAL
        freopen(".in", "r", stdin);
        freopen(".out", "w", stdout);
    #endif
    
    while (cin >> n) {
        ans = 0; // 新组,要更新ans为0,否则可能引入上一组max
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                cin >> w[i][j];

        // 把i~j行看成一个点,往右推进,形成一个一维最大sum的子序列
        for (int i = 1; i <= n; i++) {
            memset(t, 0, sizeof t);
            for (int j = i; j <= n; j++) { // 枚举i~j行的点值
                for (int k = 1; k <= n; k++)  t[k] += w[j][k];

                for (int k = 1, maxi = 0; k <= n; k++) {
                    maxi += t[k];
                    ans = max(ans, maxi);
                    if (maxi < 0) maxi = 0;
                }
            }
        }
        cout << ans << endl;
    }
    return 0;
}
posted @ 2021-07-13 18:23  Horb7  阅读(122)  评论(0编辑  收藏  举报