【题目】:

一个M*N的矩阵,找到此矩阵的一个子矩阵,并且这个子矩阵的元素的和是最大的,输出这个最大的值。
例如:3*3的矩阵:-1 3 -1
2 -1 3
-3 1 2和最大的子矩阵是:3 -1
-1 3
1 2
Input
第1行:M和N,中间用空格隔开(2 <= M,N <= 500)。
第2 - N + 1行:矩阵中的元素,每行M个数,中间用空格隔开。(-10^9 <= M[i] <= 10^9)
Output
输出和的最大值。如果所有数都是负数,就输出0。
Input示例
3 3
-1 3 -1
2 -1 3
-3 1 2
Output示例
7

View Code

【分析】:

在做这道题之前必须会最大子段和这一道题的基础,这也是动态规划中最简单的一道例题,而这道题是最大字段和的一个扩展。

例子: 0,2,7,2,-5最大的一个连续子序列之和。

分析:这五个数有这样几种和的组合:0;02;027;0272;……这样5!个数。

用数组索引来表示就是:0;0到1;0到2;0到3……1;1到2;……(这里是数组索引表示)

我们显然不能用多重循环把所有数都算出来,我们换一种角度思考,如果只有一个数字,那么最大的连续子序列数多少,还是以上面的例子为例,应该是0吧,那么现在有两个数字呢?有两种情况,一种是前面有一个数字的最大值(已求)+现在这个数字;另外一种情况是就是只有这个数字;然后算出来的最大值与只有一个数字的情况的最大值相比较;现在有三个数字,也有两种情况,一种是前面有前面两个数字的最大值+现在这个数字;或者就是只是这个数字;……

第一行:N代表这个N*N矩阵的维数int max_sum(int n)
{int i, sum = 0, max = INT_MIN;  for(i = 0; i < n; i++){  -------------------------------------------------------------------------------0if(sum < 0)sum = 0;sum += a[i];      ---------------------------------------------------------1if(sum > max)max = sum;---------------------------------------------------------2}return max;
}

  

----0----代表蓝色部分【这个不用说循环】

----1----代表橙色部分【**下面说**】

----2----代表紫色部分

我们橙色的字;因为一共有两种情况,一种是自己本身,一种是前i-1项的最大值,我们换个思路想一下,假如第1个数(索引为0的数)为负数,那么下面前两个数的最大最一定是第二个数其本身,即第二次是把sum=0;sum+=a[1]  然后记录max值。。。实际上就是假如你前n个数是正的那么可以继续加上去,如果你前n个数是负的那么把值记录一下就好了。所以只要求前n项和和最大值就好了。

当然也有人是这样写的:

int MaxSum(int n,int *a)
{  int sum=0,b=0;  for(int i=1; i<=n; i++)  {  if(b>0)  {  b+=a[i];  }  else  {  b=a[i];  }  if(b>sum)  {  sum = b;  }  }  return sum;
}

  

或者更加直白的话是这样写的,这样更加容易理解一些,看个人的编程习惯了:

int max_sum2(int n)
{b[0]=a[0];    //数组b存放前n项的最大值int maximal=b[0];for(int i=1;i<n;i++){b[i]=max(b[i-1]+a[i],a[i]); //STLif(b[i]>maximal)maximal=b[i];}return maximal;
}

  下面写二维的矩阵这道题,我们首先要做的就是降维然后像上面一样做。

先写一下思路,为什么说是最大字段和的扩展。
我们原来是一行,如果算到两行的。。。现在我们下面一行跟着上面一行一起动,再求和。即如果这个矩阵只有两行,我们先求第一行的最大值(最大字段和),然后让第二行跟着第一行一起动【其实就是把两行看成一行】,求出两行的最大值。。。以此类推。
我们知道一维时候的最大字段和,要有sum【前n个数之和】和最大值这两个变量,我们这里的sum怎么求呢? sum[i][j]从表示第i行第j个元素变成表示第i行前j个元素和,这样sum[k][j]-sum[k][i]就可以表示第k行从i->j列的元素和。

科幻小说《三体》中描绘了恢弘壮丽的“降维攻击”的场景: 
“歌者”随手抛下了一张“二向箔”,整个银河系的三维空间奔腾汹涌地流入二向箔,塌缩成一个二维平面,三维结构被碾压在二维平面之上。同时,这一降维过程是全息的,所有的三维信息被保留在碾压后的二维空间里。这种致命的攻击令攻击者和被攻击者同归于尽,玉石俱焚,其结局黑暗得令人窒息。

我们知道,在线性空间的最大子段和,已有线性时间的DP算法。有没有可能,把二维的最大矩阵和转化为一维的最大子段和呢?

答案是肯定的,算法有着似三体描述的未来武器般的威力

对于一个子矩阵,有四个属性:起始行位置 i , 结束行位置 j, 起始列 x, 结束列 y

我们可以枚举i, j, 然后对后列进行降维压缩

再对降维后的一维数组进行最大子序列和动态规划就行了。

#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
#include<stack>
#define maxn 505
#define maxm 50005
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;int n,m;
int a;
int dp[maxn][maxn];
//int sum[maxn][maxn];int main()
{while(cin>>m>>n){memset(dp,0,sizeof(dp));for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){scanf("%d",&a);dp[i][j]=dp[i-1][j]+a;////让dp存放第i行的前j项和//其实我们用数学之美来看为什么要这样存放,你一维的时候存放的是一维的数据//二维的时候再存放一维的数据就是错误的,二维的时候就应该存放二维的数据//当然你也不要去存放前i行前j列之和,这个相比于前面的就是三维的数据了//如果你算的是长方体的数据,这样应该是对的,那么我们的数组也应该是三维的了
            }}//s数组是用来记录起点行到终点行的每一竖条的数值和的,ans是用来记录不同的子矩阵和的(需要相邻竖条相加),int ans=0;for(int i=1;i<=n;i++){//这n行数有这样几种和的组合:0;02;027;0272;……这样n!个数。//用数组索引来表示就是:0;0到1;0到2;0到3……1;1到2;……(这里是数组索引表示)for(int j=i;j<=n;j++){//前面两层循环是列循环,比如三列的话就是,1-2列,1-3列,2-3列int sum=0;for(int k=1;k<=m;k++){sum+=(dp[j][k]-dp[i-1][k]);//对i到j行矩阵进行降维操作if(sum<0) sum=0;if(sum>ans) ans=sum;//ans=max(ans,sum);
                }}}printf("%d\n",ans);}return 0;
}

注意行列n,m输入顺序

转载于:https://www.cnblogs.com/Roni-i/p/8946518.html

51nod 1051 最大子矩阵和 【最大子段和DP变形/降维】相关推荐

  1. Bailian2766 最大子矩阵【最大子段和+DP】

    最大子矩阵 总时间限制: 1000ms 内存限制: 65536kB 描述 已知矩阵的大小定义为矩阵中所有元素的和.给定一个矩阵,你的任务是找到最大的非空(大小至少是1 * 1)子矩阵. 比如,如下4 ...

  2. 51Nod-1051 最大子矩阵和【最大子段和+DP】

    1051 最大子矩阵和 基准时间限制:2秒 空间限制:131072KB 分值:40难度:4级算法题 一个M*N的矩阵,找到此矩阵的一个子矩阵,并且这个子矩阵的元素的和是最大的,输出这个最大的值. 例如 ...

  3. UVA10074 Take the Land【最大子段和+DP】

    The poor man went to the King and said, "Lord, I cannot maintain my family. Please give me some ...

  4. NUC1157 To the Max【最大子段和+DP】

    To the Max 时间限制: 1000ms 内存限制: 65536KB 通过次数: 1总提交次数: 1 问题描述 Given a two-dimensional array of positive ...

  5. 【51nod - 1050】循环数组最大子段和(dp)

    题干: N个整数组成的循环序列a[1],a[2],a[3],-,a[n],求该序列如a[i]+a[i+1]+-+a[j]的连续的子段和的最大值(循环序列是指n个数围成一个圈,因此需要考虑a[n-1], ...

  6. Luogu P1115 最大子段和(dp 贪心)

    P1115 最大子段和 题目描述 给出一段序列,选出其中连续且非空的一段使得这段和最大. 输入输出格式 输入格式: 输入文件maxsum1.in的第一行是一个正整数N,表示了序列的长度. 第2行包含N ...

  7. 【vijos P1914】【codevs 3904】[NOIP2014 普及组T4]子矩阵(dfs+状压dp)

    P1914子矩阵 Accepted 标签:NOIP普及组2014[显示标签] 描述 给出如下定义: 子矩阵:从一个矩阵当中选取某些行和某些列交叉位置所组成的新矩阵(保持行与 列的相对顺序)被称为原矩阵 ...

  8. 51Nod-1050 循环数组最大段和【最大子段和+最小子段和+DP】

    1050 循环数组最大子段和 基准时间限制:1 秒 空间限制:131072 KB 分值: 10 难度:2级算法题 N个整数组成的循环序列a[1],a[2],a[3],-,a[n],求该序列如a[i]+ ...

  9. 51nod 1201:整数划分 超级好的DP题目

    1201 整数划分 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题  收藏  关注 将N分为若干个不同整数的和,有多少种不同的划分方式,例如:n = 6,{6} { ...

  10. 51Nod 1322 - 关于树的函数(树DP)

    [题目描述] [思路] 看了大佬的题解才想明白的,f_zyj大佬的题解 两棵树,对第一棵树暴力枚举所有边,拆掉这条边后的两个子树对应两个集合 A1,B1A1,B1A1,B1,用 dfsdfsdfs 枚 ...

最新文章

  1. 从熵到交叉熵损失的直观通俗的解释
  2. w10安装ubuntu_Win10 + Ubuntu 16.04双系统完美安装教程【详细】
  3. (92)低速接口UART、IIC、SPI介绍,面试必问(十六)(第19天)
  4. 【转】在Linux下编译与执行Java程序
  5. 2019年终总结——工作第二年
  6. 某度起诉“文库下载神器”,索赔300万
  7. percentile函数mysql_SQL使用窗口函数计算百分位数
  8. java 检测ip网速_网站节点测速,网站节点测速,测试网站的真实打开时间
  9. python读书心得体会范文_读书心得体会600字_关于读书的心得体会范文(3篇)
  10. 收集英语单词及释义的前期准备工作
  11. java.lang.NoSuchMethodError: org.springframework.http.MediaType.getCharSet()Ljava/nio/charset/Charse
  12. 前缀和(C/C++)
  13. 研制过程评审活动(二)方案阶段
  14. d3中文案例_D3.js实现动态仪表盘案列
  15. 初级JAVA程序员应该掌握的NPE知识
  16. 百度网盘如何加好友?
  17. VBA通达信股票交易接口获得方法
  18. D. For Gamers. By Gamers.
  19. 逻辑回归——18-19NBA常规赛下半程赛事胜负预测
  20. 如何做一个基于微信家政服务预约小程序系统毕业设计毕设作品

热门文章

  1. 火狐浏览器_全球知名度非常高的火狐浏览器,它好在哪呢?
  2. oracle脑裂复现,Oracle rac集群环境中的特殊问题
  3. python等比例压缩图片_Python批量按比例缩小图片脚本分享
  4. abp vnext修改密码策略
  5. Mysql 查询某个字段最长的记录
  6. 验证码重新发送倒计时
  7. VirtualBox安装MAC虚拟机,屏幕分辨率小,扩大的办法
  8. 全网首发:怎样制作CDKEY(0)-目录
  9. gdb问题解决办法:no debugging symbols found
  10. 期待人工智能在合作时的表现