10th 【动态规划】最大子矩阵和

                                   最大子矩阵

题目描述

给定一个整数二维数组。子矩形是在整个阵列中,一个连续的大小为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。表示正方形二维数组的大小。 之后N行是一个N×N的整数数组。数组中的数字将在区间[- 127,127 ]

输出描述

输出最大子矩形之和。

样例输入

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

样例输出

15

数据范围及描述

N <=100

首先我们需要了解最大子段和。

举一个例子,有7个数如 


1 -3 4 -2 8 -7 8 


那么可以轻松地求最大字段和。 


如果sum一直加到小于0,那么说明前面的字段都毫无意义,否则就继续往后加,中途每次都比较最大值。

下面我们简单模拟一下,设一个当前记录和的变量sum


sum+=a[1]  ->   sum=0+1=1                                  max=1
sum+=a[2]  ->   sum=1-3=-2                                 max=1
sum+=a[3]  ->   因为sum<0,所以sum=0+4=4   max=4
sum+=a[4]  ->   sum=4-2=2                                  max=4
sum+=a[5]  ->   sum=2+8=10                              max=10
sum+=a[6]  ->   sum=10-7=3                               max=10
sum+=a[7]  ->   sum=3+8=11                              max=11

这样就很清楚的求出了最大字段和,下面是基础程序 


int maxsub(int a[],int n)    
{
     int i,max=0,sum=0;
     for(i=0;i<n;i++)
     {
         if(sum>0)     sum+=a[i];
         else          sum=a[i];
         if(sum>max)   max=sum;
    }
    return max;
 }


那么这里是二维的一个矩阵,我们可以把他压缩成一维的就可以做最大字段和了。


 怎么压缩成一维的呢,就是把n行变成1行,然后就变成了n个数的数列。


 那么首先需要确定行的起点,然后需要确定行的终点,把同一列的从起始行到终止行都加起来,变成一个数,然后再求最大字段和,说明这是一个n^3的算法。


 举一个例子
 1 -2 3 
 -2 4 -1
 3  -2 0 
 设一个数组f[n]


 则f[1-3]的变化如下
 1.                                                        1  -2 3
 2.      (1-2)(-2+4)(3-1)                     -1  2  2
 3.      (1-2+3)(-2+4-2)(3-1+0)        -2  0  2
          (等于(-1+3)(2-2)(2+0))  
 4.                                                       -2  4 -1
 5.        (-2+3)(4-2)(-1+0)                 1  2 -1
 6.                                                        -3 -2 0
 六种情况不重不漏,分别求最大字段和就行了。
 

 for(int i=1;i<=n;i++){ 起点  
           将f[]清空为0 
            for(int j=i;j<=n;j++){ 从起点开始下拉   
                for(int k=1;k<=n;k++)
                    f[k]+=a[j][k];  f[k]为从a[i][k]到a[j][k]的和 
                然后求一个f数组的最大子段和   
           }
       }

就这样很简单的完成了问题
  三维的题目也是这样,压缩一个面。
   所以下面是我的程序

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<cmath> 
using namespace std;
int n,a[105][105],f[105];
const int inf=-2147483647;

int max(int a,int b)
{return (a>b?a:b);}

int main(){
    freopen("test2.in","r",stdin);
    freopen("test2.out","w",stdout);
    int ans=inf;
    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++){  
            for(int j=1;j<=n;j++)   
            f[j]=0;
            for(int j=i;j<=n;j++){     
                for(int k=1;k<=n;k++)
                    f[k]+=a[j][k];
                int mx=inf,sum=0;
                for(int k=1;k<=n;k++)
                  {sum=(sum>0?sum+f[k]:f[k]); 
                   mx=max(mx,sum);}
                ans=max(mx,ans);      
           }
       }
        cout<<ans<<endl;
    return 0;
    }




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值