最大子矩阵和 = 前缀和 + 最大子段和
简单来说这道题就是求一个
(因为求的是黑色石板与白色石板的数量差,所以代表白色石板的“0”可以看作 -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
在每一种情况里(我们这里有三种),我们还要找出一个最大的子矩阵,当然,这只是一种情况的最大子矩阵(局部最大),不一定是全局最大。
但是,如果我们知道每一种情况的最大,要找出全局最大,那就小菜一碟儿了。
在讲在一个特殊情况下求最大子矩阵之前,先讲一个事实:
假设这个最大子矩阵的维数是一维,要找出最大子矩阵, 原理与求“最大子段和问题”是一样的。最大子段和问题的递推公式是 :
指的是从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);
但是,原始矩阵可以是二维的。假设原始矩阵是一个
如果是
如果是
如果是
为了能够找出最大的子矩阵,我们需要考虑所有的情况。
假设这个子矩阵是
为了找出在原始矩阵里的最大子矩阵,我们要遍历所有的子矩阵的可能情况,也就是说,我们要考虑这个子矩阵有可能只有 1 行,2 行……到
所以我边输入,边预处理出了每一列的前缀和:
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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!