经典算法:最大子段和:
状态转移方程:
若a[i-1]>0,a[i]=a[i]+a[i-1], 否则a[i]=a[i]
最后取a[i]的最大值即可
#include<bits/stdc++.h>
using namespace std;
int a[200001];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
int ans=-1e8;
for(int i=2;i<=n;i++)
{
if(a[i-1]>0)
{
a[i]=a[i]+a[i-1];
}
ans=max(ans,a[i]);
}
cout<<ans;
return 0;
}
如果再简洁一下就是:
for(int i=1;i<=n;i++){
dp[i]=max(a[i],dp[i-1]+a[i]);
}
ps:其实就是设置一个sum一直加,如果加到<=0,就重置成0,然后取过程中的最大值即可
最大子矩阵和
写法1:
思路来源
枚举一个起点行(1-n)
然后从该【起点行】一直到【末尾行】,将元素累加前缀到末尾行上
对于每一次行累加,都对该行求一次【一维最大子段和】
求过程中的最大值即可
#include<bits/stdc++.h>
using namespace std;
int n,m;
int res=-1e8;
int a[1001][1001];
int b[1001][1001];
int dp[1001];
void slove(int j) {
memset(dp,0,sizeof(dp));
for(int i=1;i<=m;i++)
{
dp[i]=max(b[j][i],dp[i-1]+b[j][i]);//求一维最大子段和
res=max(res,dp[i]);
}
}
int main() {
cin>>n>>m;
for(int i=1; i<=n; i++) {
for(int j=1; j<=m; j++) {
cin>>a[i][j];
}
}
for(int i=1; i<=n; i++) { //枚举前缀和的起点【行】
memset(b,0,sizeof(b));
for(int j=i; j<=n; j++) { //开始进行后 j行的前缀和累加,计算后j行的前缀和!
for(int k=1; k<=m; k++) {
b[j][k]=a[j][k]+b[j-1][k];
}
slove(j);//对每一次的前缀和数组进行求【一维最大子段和操作】
}
}
cout<<res;
return 0;
}
写法2:
1.先进行预处理,对每一个行的元素:
每一行的元素等于这一行当前的前缀和
这样方便后续对每一行进行差分求出该行的区间和
2.然后枚举 i,j(1-m) (j>=i) 通过对每一行中的元素差分,求出每一行【i,j】区间内的元素和
3.枚举 k(1-n) ,对每一行[i,j]内的元素和,求他们的【一维最大子段和】
其实就是相当于求整个矩阵 [i,j]列范围内的最大和,然后又因为不断的
枚举i,j,最后求出整个二维数组的最大子矩阵和 ,求过程中的最大值即可
#include<bits/stdc++.h>
using namespace std;
int a[1001][1001];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
a[i][j]+=a[i][j-1];
}
}
int ans=-1e8;
for(int i=1;i<=m;i++)
{
for(int j=i;j<=m;j++)
{
int temp=0;
for(int k=1;k<=n;k++)
{
temp+=a[k][j]-a[k][i-1];
if(temp<0)
temp=0;
ans=max(temp,ans);
}
}
}
cout<<ans;
return 0;
}