最大子矩阵和

//压缩行,DP列,备忘录max
#include<iostream>
#include<cstring>
#include<cstdio>
#include <climits>
using namespace std;

int a[105][105],b[105][105];

int main()
{
    //freopen("最大子矩阵和.txt","r",stdin);
    int t,m,n,i,j,max;
    cin>>t;
    while(t--){
        max=INT_MIN;  //INT_MIN 表示整型的无穷小 包含在#include<climits> ,相应的也有INT_MAX
        memset(b,0,sizeof(b));
        cin>>m>>n;
        for(i=1;i<=m;i++)
            for(j=1;j<=n;j++)
                cin>>a[i][j];
        for(i=1;i<=m;i++)
            for(j=1;j<=n;j++)
                b[i][j]=b[i][j-1]+a[i][j];


        for(i=1;i<=n;i++){  //子矩阵从第i列开始累加
            for(j=i;j<=n;j++){  //子矩阵从第i列累加到第j列
                //以下是求最大子串和(一维)
                int num=0;  //当前最大值
                for(int k=1;k<=m;k++){  //k从第1行到第m行做一维的DP
                    if(num>0)
                        num+=(b[k][j]-b[k][i-1]); //要注意是DP(b[k][j]-b[k][i-1])(DP列 = 行变换,即 k 变换)
                    else
                        num=(b[k][j]-b[k][i-1]);
                    if(num>max)
                        max=num;
                }
            }
        }
        cout<<max<<endl;

    }
    return 0;
}


总结:状态压缩DP。这道题可以看做是一维最大子串和的二维扩展,一维最大子串和采用了备忘录似的DP,其采用的是用一个 b[i] 来记录:扫描到第 i 个元素时,其最优子串和的状态转移方程是:b[i] = max { a[i] , b[i] + a[i] } ,运用到这道题中,需要将二维的压缩成一维,然后进行如上算法。


举例:原始矩阵用 a[][] 表示

0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2   ………………【1】


其拥有最大和的子矩阵为:
9 2
-4 1
-1 8
其和为15。


解题步骤:(1)压缩行;(2)DP列;(3)备忘录max

(1)压缩行:

将每行自左向右做累加,存入 b[i][j] 中。于是上述矩阵就变为:
0 -2 -9 -9
9 11 5 7
-4 -3 -7 -6
-1 7 7 5   ………………【2】
 
b[i][j] 表示第 i 行,自第 1 列累加到第 j 列的和。
如果想表示第 i 行,自第 j 列累加到第 k 列的和(j<=k),我们就可以用如下表达式:
b[i][k] - b[i][j-1] = 第 1 列累加到第 k 列的和 - 第 1 列累加到第 j-1 列的和
 
这样做的好处就是可以将求第 i 行的第 j 列累加到第 k 列这个过程的算法复杂度从O(n)压缩到O(1)。


(2)DP列:

得到矩阵【2】之后,按照一维DP的方式,对每列从第 1 行往第 M 行做DP。比如矩阵【2】中的第一列:0 9 -4 -1。
按照一维DP之后的b[]数组为:0 9 5 4,最大值为9,这就表明矩阵【1】中子矩阵
0
9
-4
-1
中的最大子矩阵和为9。
 
循环DP,共三层循环。
最外层 i 循环 1--N,表明子矩阵是从第 i 列开始累加的。
第二层 j 循环 i--N,表明子矩阵是从第 i 列累加到第 j 列。
第三层 k 从 1 到 M 做一维DP。
 
所以其复杂度为O(n^3)。如果穷举的话,需要确定子矩阵左上角坐标x,y,需要O(n^2);需要确定右下角坐标x,y,需要O(n^2);需要循环计算子矩阵和,O(n^2);一共是O(n^6)。


(3)备忘录max:

我们这种DP的解法是O(n^3)的时间复杂度,但是存储空间耗费不小,存 b[][],还要存做列DP之后的每行最优解。所以实际需要三维数组b 来存放。但是我们采用一个备忘录变量值sum,在每次DP后记录其值,反复比较保留最大的sum。最后留下的即为最大子矩阵和。




准备知识参看最大子串和 






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值