TK题库 1133 最大子阵和(动态规划DP 最大字段和)

原题

 最大子阵和

时间限制: 1 Sec 内存限制: 32 MB


题目描述

有一个包含正数和负数的二维数组。一个子矩阵是指在该二维数组里,任意相邻的下标是1*1或更大的子数组。一个子矩阵的和是指该子矩阵中所有元素的和。本题中,把具有最大和的子矩阵称为最大子矩阵。
例如:
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
这个数组的最大子矩阵为:
9 2
-4 1
-1 8
其和为15。

输入

输入包含多组测试数据。每组输入的第一行是一个正整数N(1<=N<=100),表示二维方阵的大小。接下来N行每行输入N个整数,表示数组元素,范围为[-127,127]。

输出

输出最大子阵和。

样例输入

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

样例输出

15

涉及知识及算法


首先,这个子矩阵可以是任意大小的,而且起始点也可以在任何地方,所以,要把最大子矩阵找出来,我们要考虑多种情况。
假定原始矩阵的行数为M,那么对于子矩阵,它的行数可以是1到M的任何一个数。
对于一个K行(K < M)的子矩阵,它的第一行可以是原始矩阵的第1行到 M - K + 1 的任意一行。

假设这个最大子矩阵的维数是一维,要找出最大子矩阵, 原理与求“最大子段和问题” 是一样的。
最大子段和问题的递推公式是 b[j]=max{b[j-1]+a[j], a[j]},b[j] 指的是从0开始到j的最大子段和。例子:
假设一个一维子矩阵为:[9, 2, -6, 2], 那么b[] = {9, 11, 5, 7}, 那么最大字段和为11, 如果找最大子矩阵的话,那么这个子矩阵是 [9, 2] 。

但是,原始矩阵可以是二维的。假设原始矩阵是一个3 * n 的矩阵,那么它的子矩阵可以是 1 * k, 2 * k, 3 * k,(1 <= k <= n)。 
如果是1 * k,这里有3种情况:子矩阵在第一行,子矩阵在第二行,子矩阵在第三行。
如果是2 * k,这里有两种情况,子矩阵在第一、二行,子矩阵在第二、三行。
如果是3 * k,只有一种情况。

假设这个子矩阵是 2 *k, 也就是说它只有两行,如果我们把这两行上下相加,情况就和求“最大子段和问题” 又是一样的了。
我们要考虑这个子矩阵有可能只有1行,2行,。。。到n行。而在每一种情况下,我们都要把它所对应的矩阵部分上下相加才求局部最大子矩阵。
比如,假设子矩阵是一个3*k的矩阵,而且,它的一行是原始矩阵的第二行,那么,我们就要在
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
里找最大的子矩阵。
如果把它上下相加,我们就变成了 4, 11, -10,1, 从这个数列里可以看出,在这种情况下,最大子矩阵是一个3*2的矩阵,最大和是15.
为了能够在原始矩阵里很快得到从 i 行到 j 行 的上下值之和,我们这里用到了一个辅助矩阵t,它是原始矩阵从上到下加下来的。
如果我们要求第 i 行到第 j 行之间上下值的和,我们可以通过t[j][f] - t[i-1][f] 得到。

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXN=100;
int N;
//最大字段和数组
int maxSub[MAXN];
//存储原始矩阵
int m[MAXN][MAXN];
//从首行到当前行的和矩阵
int t[MAXN][MAXN];
//从i行到第j行所对应的矩阵上下值的和
int ij[MAXN];
//最大字段和
int maxSubsequence(int a[])
{
    int Max=maxSub[0]=a[0];
    for(int i=1;i<N;i++)
    {
        maxSub[i]=(maxSub[i-1]>0)?(maxSub[i-1]+a[i]):a[i];
        if(Max<maxSub[i])
        {
            Max=maxSub[i];
        }
    }
    return Max;
}

int subMaxMatrix()
{
    for(int i=1;i<N;i++)
    {
        for(int j=0;j<N;j++)
        {
            t[i][j]+=t[i-1][j];
        }
    }
    int Max=-999;
    for(int i=0;i<N;i++)
    {
        for(int j=i;j<N;j++)
        {
            for(int f=0;f<N;f++)
            {
                if(i==0)
                {
                    ij[f]=t[j][f];
                }
                else
                {
                    ij[f]=t[j][f]-t[i-1][f];
                }
            }
            int result=maxSubsequence(ij);
            if(result>Max)
            {
                Max=result;
            }
        }
    }
    return Max;
}

int main()
{
    //freopen("in.txt","r",stdin);
    while(scanf("%d",&N)==1)
    {
        for(int i=0;i<N;i++)
        {
            for(int j=0;j<N;j++)
            {
                scanf("%d",&m[i][j]);
                t[i][j]=m[i][j];
            }
        }
        printf("%d\n",subMaxMatrix());
    }
    return 0;
}
文章转载自CSDN博主beiyeqingteng,链接http://blog.csdn.net/beiyeqingteng/article/details/7056687,表示感谢。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值