首页 > 其他分享 >最大子矩阵和 = 前缀和 + 最大子段和

最大子矩阵和 = 前缀和 + 最大子段和

时间:2022-10-06 17:01:18浏览次数:55  
标签:找出 最大 子段 int 矩阵 cdot 前缀

简单来说这道题就是求一个 \(N \times M\) 的矩阵的最大子矩阵和。
(因为求的是黑色石板与白色石板的数量差,所以代表白色石板的“0”可以看作 -1,这样就将问题转化为了求最大子矩阵和)

思路:

首先,这个子矩阵可以是任意大小的,而且起始点也可以在任何地方,所以,要把最大子矩阵找出来,我们要考虑多种情况。

假定原始矩阵的行数为 \(M\),那么对于子矩阵,它的行数可以是 1 到 \(M\) 的任何一个数,而且,对于一个 \(K\) 行(\(K \le M\))的子矩阵,它的第一行可以是原始矩阵的第 1 行到 \(M - K + 1\) 的任意一行。

例子:

1 -1 1 1 -1
-1 1 1 1 1
1 1 1 1 -1
1 -1 1 -1 1

对于样例二的矩阵,如果子矩阵的行数是 2,那么它可以是下面几个矩阵的子矩阵:

1 -1 1 1 -1
-1 1 1 1 1

或者

-1 1 1 1 1
1 1 1 1 -1

或者

1 1 1 1 -1
1 -1 1 -1 1

在每一种情况里(我们这里有三种),我们还要找出一个最大的子矩阵,当然,这只是一种情况的最大子矩阵(局部最大),不一定是全局最大。

但是,如果我们知道每一种情况的最大,要找出全局最大,那就小菜一碟儿了。

在讲在一个特殊情况下求最大子矩阵之前,先讲一个事实:

假设这个最大子矩阵的维数是一维,要找出最大子矩阵, 原理与求“最大子段和问题”是一样的。最大子段和问题的递推公式是 :

\[b[j]=\max(b[j-1]+a[j],a[j]) \]

\(b[j]\) 指的是从1开始到 \(j\) 的最大子段和。

例子:

假设原始矩阵为:[1 ,-1 ,1 ,1 ,-1], 那么 \(b[]\) = {1,0,1,2,1} , 那么最大子段和为 2,如果找最大子矩阵的话,那么这个子矩阵是 [1,-1,1,1] (或者[1,1]) 。

求最大子段和的代码如下:

int b[1000]={};
for(int k=1;k<=n;k++){
	b[k]=max(a[k],b[k-1]+a[k]);
	ans=max(ans,b[k]);
}
printf("%d",ans);

但是,原始矩阵可以是二维的。假设原始矩阵是一个 \(3 \cdot n\) 的矩阵,那么它的子矩阵可以是 \(1 \cdot k\), \(2 \cdot k\), \(3 \cdot k\)(\(1 \leq k \leq n\))。

如果是 \(1 \cdot k\),这里有3种情况:子矩阵在第一行,子矩阵在第二行,子矩阵在第三行。

如果是 \(2 \cdot k\),这里有两种情况,子矩阵在第一、二行,子矩阵在第二、三行。

如果是 \(3 \cdot k\),只有一种情况。

为了能够找出最大的子矩阵,我们需要考虑所有的情况。

假设这个子矩阵是 \(2 \cdot k\), 也就是说它只有两行,要找出最大子矩阵,我们要从左到右不断的遍历才能找出在这种情况下的最大子矩阵。如果我们把这两行上下相加,情况就和求“最大子段和问题” 又是一样的了。

为了找出在原始矩阵里的最大子矩阵,我们要遍历所有的子矩阵的可能情况,也就是说,我们要考虑这个子矩阵有可能只有 1 行,2 行……到 \(n\) 行。而在每一种情况下,我们都要把它所对应的矩阵部分上下相加才求最大子矩阵(局部)。

所以我边输入,边预处理出了每一列的前缀和:

cin>>n>>m;
for(int i=1;i<=n;i++){
	cin>>(s+1);
	for(int j=1;j<=m;j++){
		dp[j][i]+=dp[j][i-1];
		if(s[j]-'0') dp[j][i]++;
		else dp[j][i]--;
	}
}
//dp[j][i] 表示 第j列 1到第i行的前缀和

之后枚举矩阵的起点行和终点行,通过最大子段和的方法求最大子矩阵:

for(int i=1;i<=n;i++){        //枚举起点行
	for(int j=i;j<=n;j++){      //枚举终点行
		int b[1000]={};
		for(int k=1;k<=m;k++){   //前缀和+最大子段和
			b[k]=max(dp[k][j]-dp[k][i-1],b[k-1]+dp[k][j]-dp[k][i-1]);
			ans=max(ans,b[k]);
		}
	}
}

完整代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,ans;
char s[1000];
int dp[1000][1000];
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>(s+1);
		for(int j=1;j<=m;j++){
			dp[j][i]+=dp[j][i-1];
			if(s[j]-'0') dp[j][i]++;
			else dp[j][i]--;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=i;j<=n;j++){
			int b[1000]={};
			for(int k=1;k<=m;k++){
				b[k]=max(dp[k][j]-dp[k][i-1],b[k-1]+dp[k][j]-dp[k][i-1]);
				ans=max(ans,b[k]);
			}
		}
	}
	cout<<ans;
	return 0;
}

标签:找出,最大,子段,int,矩阵,cdot,前缀
From: https://www.cnblogs.com/ycw123/p/16757981.html

相关文章

  • 矩阵计算
    矩阵的二范数是他的最大特征值 https://www.zhihu.com/question/48945813/answer/113453186 矩阵的F范数等于矩阵的迹: ......
  • 关于Hessian矩阵的图像增强
    文章目录​​1.数字图像处理之尺度空间理论​​​​2.基于尺度理论的Hessian简化算法​​​​3.基于Hessian矩阵的图像增强​​本文是关于图像增强方面的知识。关于Ret......
  • 【机器学习中的矩阵求导】(八)标量函数f(x)的雅克比矩阵(迹函数)
    学习总结交换律:,需要满足、同维度行列式微分:文章目录​​学习总结​​​​一、标量函数的雅克比函数​​​​二、关于迹函数的性质​​​​2.1常用性质​​​​2.2迹函数的......
  • [CG从零开始] 5. 搞清 MVP 矩阵理论 + 实践
    在4中成功绘制了三角形以后,下面我们来加载一个fbx文件,然后构建MVP变换(model-view-projection)。简单介绍一下:从我们拿到模型(主要是网格信息)文件开始,模型网格(Mesh)里......
  • 位姿矩阵求逆(转)
    位姿矩阵(或者称为旋转平移矩阵)即若干旋转矩阵和平移矩阵的合成,可以用来描述物体的方位。位姿矩阵具有形式:   且其中3*3部分   是一个正交阵,表示合旋转。(......
  • LCP 最长公共前缀(一个字符串中,两个位置的后缀的最长公共前缀)
    LCP也可以用来进行一个字符串的子字符串的比较需要预处理lcp[i][j]数组,表示从i开始的后缀和从j开始的后缀的最长公共前缀lcp[i][j]可以从lcp[i+1][j+1]递推过来O(n^2)预......
  • 数据组中的矩阵两个对角线的和(优化版)
    #include<stdio.h>intmain(){ intMAP[4][4]={12,11,14,15, 21,22,23,24, 65,55,66,77, 87,98,51,32}; inti,j; intsu......
  • 矩阵乘法(快速幂)结合dp结合除法逆元的例题
    https://atcoder.jp/contests/abc271/tasks/abc271_g题目的思路为:构建dp矩阵,dp[i][j][k]表示开始前停在j,结束后停在k,且停下时恰好出现2^i次访问的概率则dp[i]=dp[i-1]*d......
  • 前缀、中缀、后缀表达式
    前缀、中缀、后缀表达式是对表达式的不同记法,其区别在于运算符相对于操作数的位置不同,前缀表达式的运算符位于操作数之前,中缀和后缀同理举例:中缀表达式:1+(2+3)×4-5......
  • 树上前缀和
    设sum[i]表示节点i到根节点的权值总和。如果是点权,x,y路径上的和为sum[x]+sum[y]-sum[lca]-sum[fa[lca]]如果是边权,x,y路径上的和为sum[x]+sum[y]-2*sum[lca]边前缀和......