最大子矩阵和问题归纳总结

2024-09-07 18:32

本文主要是介绍最大子矩阵和问题归纳总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一,最大子矩阵问题:
给定一个n*n(0< n <=100)的矩阵,请找到此矩阵的一个子矩阵,并且此子矩阵的各个元素的和最大,输出这个最大的值。
Example:
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
其中左上角的子矩阵:
9 2
-4 1
-1 8
此子矩阵的值为9+2+(-4)+1+(-1)+8=15。

二,分析
子矩阵是在矩阵选取部份行、列所组成的新矩阵。
我们首先想到的方法就是穷举一个矩阵的所有子矩阵,然而一个n*n的矩阵的子矩阵的个数当n比较大时时一个很大的数字 O(n^2*n^2),显然此方法不可行。怎么使得问题的复杂度降低呢?对了,相信大家应该知道了,用动态规划。对于此题,怎么使用动态规划呢?

    请先参考-->最大子段和问题这个问题与最大子段有什么联系呢?

1、首先考虑一维的最大子段和问题,给出一个序列a[0],a[1],a[2]…a[n],求出连续的一段,使其总和最大。

a[i]表示第i个元素
dp[i]表示以a[i]结尾的最大子段和

dp[i] = max{a[i], dp[i-1] + a[i]}

解释一下方程:

如果dp[i-1] > 0,则 dp[i] = dp[i-1] + a[i]
如果dp[i-1] < 0,则 dp[i] = a[i]

因为不用记录位置信息,所以dp[]可以用一个变量dp代替:

如果dp > 0,则dp += a[i]
如果dp < 0,则dp = a[i]

2、考虑二维的最大子矩阵问题

我们可以利用矩阵压缩把二维的问题转化为一维的最大子段和问题。因为是矩阵和,所以我们可以把这个矩形的高压缩成1,用加法就行了。

假设最大子矩阵的结果为从第r行到k行、从第i列到j列的子矩阵,如下所示(ari表示a[r][i],假设数组下标从1开始):

| a11 …… a1i ……a1j ……a1n |
| a21 …… a2i ……a2j ……a2n |
| . . . . . . . |
| . . . . . . . |
| ar1 …… ari ……arj ……arn |
| . . . . . . . |
| . . . . . . . |
| ak1 …… aki ……akj ……akn |
| . . . . . . . |
| an1 …… ani ……anj ……ann |

那么我们将从第r行到第k行的每一行中相同列的加起来,可以得到一个一维数组如下:
(ar1+……+ak1, ar2+……+ak2, ……,arn+……+akn)
由此我们可以看出最后所求的就是此一维数组的最大子段和问题,到此我们已经将问题转化为上面的已经解决了的问题了。

poj1050:给一个n*n(1<=n<=100)的矩阵,求最大子矩阵和

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
int mp[100][100];
int temp[200];
const  int mod = 1e9+7;int solve(int *a,int n)
{int dp = 0,Max = 0;for(int i = 0; i < n; i++){if(dp > 0) dp += a[i];else dp = a[i];Max = max(Max, dp);}return Max;
}int main()
{#ifdef xxzfreopen("in.txt","r",stdin);#endif // xxzint n;while(~scanf("%d",&n)){for(int i = 0; i < n; i++)for(int j = 0; j < n; j++)cin>>mp[i][j];int max_ans = 0;for(int i = 0; i < n; i++){memset(temp,0,sizeof(temp));for(int j = i;j < n; j++){for(int k = 0; k < n; k++)temp[k] += mp[j][k];int ans = solve(temp,n);max_ans = max(max_ans,ans);}}cout<<max_ans<<endl;}return 0;
}

HDU1559:给出n*m(0< n,m<=1000)的矩阵,求规格为x*y的小矩阵最大和为多少。
这题相比上题增加了限制条件,也就是上面那种的特殊情况,如用上面算法时间复杂度是O(n*n*m)则会超时,由于题目对小矩阵有了限制,我们可以用DP做,令a[i][j]为1<=s<=i,1<=t<=j所有元素的和,可以在线处理,那么就可以通过加减来求出当前子矩阵和,就好像计算几个矩阵面积一样(某部分覆盖在一起,所以相加后减去重复的面积)。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
int mp[1000][1000];
int temp[200];
const  int mod = 1e9+7;int solve(int *a,int n)
{int dp = 0,Max = 0;for(int i = 0; i < n; i++){if(dp > 0) dp += a[i];else dp = a[i];Max = max(Max, dp);}return Max;
}int main()
{
#ifdef xxzfreopen("in.txt","r",stdin);
#endif // xxzint T;scanf("%d",&T);while(T--){int n,m,x,y;cin>>n>>m>>x>>y;int Max = 0;for(int i = 1; i <= n; i++){for(int j = 1; j <= m; j++){cin>>mp[i][j];mp[i][j] += mp[i-1][j] + mp[i][j-1] - mp[i-1][j-1];if(i >= x && j >= y){int ans = mp[i][j] - mp[i-x][j] - mp[i][j-y] + mp[i-x][j-y];Max = max(Max,ans);}}}cout<<Max<<endl;}return 0;
}

其实这题还可以用数状数组做,二维的裸题

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;int S;int mp[1200][1200];
int lowbit(int x)
{return x & -x;
}int  getsum(int x,int y)
{int  sum  = 0;for(int i  = x; i > 0; i -= lowbit(i))for(int j = y; j > 0; j -= lowbit(j)){sum += mp[i][j];}return sum;
}void update(int x,int y,int value)
{for(int i = x; i <= 1200; i += lowbit(i))for(int j = y; j <=1200 ; j += lowbit(j)){mp[i][j] += value;}
}int main()
{#ifdef xxzfreopen("in","r",stdin);#endif // xxzint T;scanf("%d",&T);while(T--){int m,n,x,y;scanf("%d%d%d%d",&m,&n,&x,&y);memset(mp,0,sizeof(mp));for(int i = 1; i <= m; i++){for(int j = 1; j <= n; j++){int temp;scanf("%d",&temp);update(i,j,temp);}}int ans = -1;for(int i = x; i <= m; i++){for(int j = y; j <= n; j++){ans = max(ans,getsum(i,j) - getsum(i-x,j)-getsum(i,j-y) + getsum(i-x,j-y));}}printf("%d\n",ans);}return 0;
}

这篇关于最大子矩阵和问题归纳总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1145825

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

好题——hdu2522(小数问题:求1/n的第一个循环节)

好喜欢这题,第一次做小数问题,一开始真心没思路,然后参考了网上的一些资料。 知识点***********************************无限不循环小数即无理数,不能写作两整数之比*****************************(一开始没想到,小学没学好) 此题1/n肯定是一个有限循环小数,了解这些后就能做此题了。 按照除法的机制,用一个函数表示出来就可以了,代码如下

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

购买磨轮平衡机时应该注意什么问题和技巧

在购买磨轮平衡机时,您应该注意以下几个关键点: 平衡精度 平衡精度是衡量平衡机性能的核心指标,直接影响到不平衡量的检测与校准的准确性,从而决定磨轮的振动和噪声水平。高精度的平衡机能显著减少振动和噪声,提高磨削加工的精度。 转速范围 宽广的转速范围意味着平衡机能够处理更多种类的磨轮,适应不同的工作条件和规格要求。 振动监测能力 振动监测能力是评估平衡机性能的重要因素。通过传感器实时监

缓存雪崩问题

缓存雪崩是缓存中大量key失效后当高并发到来时导致大量请求到数据库,瞬间耗尽数据库资源,导致数据库无法使用。 解决方案: 1、使用锁进行控制 2、对同一类型信息的key设置不同的过期时间 3、缓存预热 1. 什么是缓存雪崩 缓存雪崩是指在短时间内,大量缓存数据同时失效,导致所有请求直接涌向数据库,瞬间增加数据库的负载压力,可能导致数据库性能下降甚至崩溃。这种情况往往发生在缓存中大量 k

git使用的说明总结

Git使用说明 下载安装(下载地址) macOS: Git - Downloading macOS Windows: Git - Downloading Windows Linux/Unix: Git (git-scm.com) 创建新仓库 本地创建新仓库:创建新文件夹,进入文件夹目录,执行指令 git init ,用以创建新的git 克隆仓库 执行指令用以创建一个本地仓库的

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

poj 3723 kruscal,反边取最大生成树。

题意: 需要征募女兵N人,男兵M人。 每征募一个人需要花费10000美元,但是如果已经招募的人中有一些关系亲密的人,那么可以少花一些钱。 给出若干的男女之间的1~9999之间的亲密关系度,征募某个人的费用是10000 - (已经征募的人中和自己的亲密度的最大值)。 要求通过适当的招募顺序使得征募所有人的费用最小。 解析: 先设想无向图,在征募某个人a时,如果使用了a和b之间的关系

poj 3258 二分最小值最大

题意: 有一些石头排成一条线,第一个和最后一个不能去掉。 其余的共可以去掉m块,要使去掉后石头间距的最小值最大。 解析: 二分石头,最小值最大。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <c