最大子矩阵问题(最大连续子序列和,线性dp)

【题目描述】
已知矩阵的大小定义为矩阵中所有元素的和。给定一个矩阵,你的任务是找到最大的非空(大小至少是1 × 1)子矩阵。

比如,如下4 × 4的矩阵

0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
的最大子矩阵是

9 2
-4 1
-1 8
这个子矩阵的大小是15。

【输入】
输入是一个N×N的矩阵。输入的第一行给出N(0<N≤100)。再后面的若干行中,依次(首先从左到右给出第一行的N个整数,再从左到右给出第二行的N个整数……)给出矩阵中的N2个整数,整数之间由空白字符分隔(空格或者空行)。已知矩阵中整数的范围都在[−127,127]。

【输出】
输出最大子矩阵的大小。

【输入样例】
4
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
【输出样例】
15
题目分析:
这个问题需要用一个一维的最大连续子序列和的问题作为基础,状态转移方程很好理解。用dp[i]表示1到i的最大连续子序列和。dp[i]=max(dp[i-1]+a[i],a[i])
我来简单解释一下,数组a[i]中的每一个值都是确定的,而dp的值相对不确定,所以1到i的最大连续子序列和就等于1到i-1的dp[i-1]与定值a[i]的和,但当dp[i-1]<0时就可以用a[i]作为新序列的开头元素。总结下来就是这样:
dp[i]=dp[i-1]+a[i], (dp[i-1]>0)
dp[i]=a[i], (dp[i-1]<0) 就是在这两个式子中取最大值。
而最大子矩阵问题就是需要将二维的问题压缩到一维,将矩阵中所有的可能情况全部列出并用一个二维数组储存。这里设f[i][j]表示前i行j列的累加和

for(int i=1;i<=n;i++)//控制子矩阵上界
    {
        memset(f,0,sizeof(f));//每更新一次上界必须给f数组清零
        for(int j=i;j<=n;j++)
        {
            for(int k=1;k<=n;k++)//每一列
            f[j][k]=f[j-1][k]+a[j][k];
            sum(j);//求从i到j行的最大连续子段和(即最大子矩阵)
        }
    }

其中sum(int)函数是求最大连续子序列的函数,不用管。
这样就可以枚举矩阵中的所有情况了。
完整代码:

#include<iostream>
#include<cstring>
using namespace std;
int maxl=-99999;
int n;
int a[111][111],f[111][111],dp[111];
//a用来存储矩阵。f[i][j]表示前i行j列的累加和,dp[i]表示前i行最大子矩阵的和。
void sum(int j)
{
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=n;i++)
    {
        dp[i]=max(dp[i-1]+f[j][i],f[j][i]);
        maxl=max(dp[i],maxl);
    }
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        cin>>a[i][j];
    for(int i=1;i<=n;i++)//控制子矩阵上界
    {
        memset(f,0,sizeof(f));//每更新一次上界必须给f数组清零
        for(int j=i;j<=n;j++)
        {
            for(int k=1;k<=n;k++)//每一列
            f[j][k]=f[j-1][k]+a[j][k];
            sum(j);//求从i到j行的最大连续子段和(即最大子矩阵)
        }
    }
    cout<<maxl;
    return 0;
}
发布了42 篇原创文章 · 获赞 42 · 访问量 9319

猜你喜欢

转载自blog.csdn.net/amazingee/article/details/104311743
今日推荐