就像最大子矩阵和一样降维
先不考虑p
枚举行i,j,sum[k]=∑jx=imat[x][k]
则对sum[]求一遍最大字段和即可得到最大子矩阵
这里同理,令minVal[k]=min(mat[x][k]|i<=x<=j)
d[i][0]=以第i列为终点,未修改过某点为p的最大子矩阵和值
d[i][1]=以第i列为终点,修改过某点为p的最大子矩阵和值
d[i][0]=max(d[i−1][0],0)+sum[i]
d[i][1]=max{d[i−1][1]+sum[i],max(d[i−1][0],0)+sum[i]−minVal[i]+p}
当选择的行数量不是n行时
允许修改p时,最大子矩阵和={max(d[i−1][0],d[i−1][1]) | 0<=i<n})
对于选择n行且选择了m列,此时必须要选一个值修改为p
方便起见对于选择了n行时,继续枚举选择的列的区间
O(n2)
复杂度
O(n3)
#include<stdio.h>
#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define pll pair<ll,ll>
#define MEM(a,x) memset(a,x,sizeof(a))
#define lowbit(x) ((x)&-(x))
using namespace std;
const int INF = 1e9+7;
const int inf=INF;
const int N = 300 + 5;
int mat[N][N];
int sum[N];
int d[N][2];
int minVal[N];
int dp(int n,int*sum,int p){
d[0][0]=sum[0];
d[0][1]=sum[0]-minVal[0]+p;
for(int i=1;i<n;++i){
d[i][0]=max(d[i-1][0],0)+sum[i];
d[i][1]=max( d[i-1][1]+sum[i], max(d[i-1][0],0)+ sum[i]-minVal[i]+p );
}
int ans=-inf;
for(int i=0;i<n;++i){
ans=max(ans,max(d[i][0],d[i][1]));
}
return ans;
}
int tePan(int m,int p){//选择了0~n-1行时
int ans=-inf;
for(int i=0;i<m;++i){
int minX=inf;
int sumX=0;
for(int j=i;j<m;++j){
minX=min(minX,minVal[j]);
sumX+=sum[j];
if(i==0&&j==m-1){
ans=max(ans,sumX-minX+p);
}
else{
int tsumX=max(sumX-minX+p,sumX);
ans=max(ans,tsumX);
}
}
}
return ans;
}
int slove(int n,int m,int p){
int ans=-inf;
for(int i=0;i<n;++i){
fill(sum,sum+m+1,0);
fill(minVal,minVal+m+1,inf);
for(int j=i;j<n;++j){
for(int k=0;k<m;++k){
sum[k]+=mat[j][k];
minVal[k]=min(minVal[k],mat[j][k]);
}
if(i==0&&j==n-1){
ans=max(ans,tePan(m,p));
}
else{
ans=max(dp(m,sum,p),ans);
}
}
}
return ans;
}
int main(){
//freopen("/home/lu/code/r.txt","r",stdin);
//freopen("/home/lu/code/w.txt","w",stdout);
int n,m,p;
while(~scanf("%d%d%d",&n,&m,&p)){
for(int i=0;i<n;++i){
for(int j=0;j<m;++j){
scanf("%d",&mat[i][j]);
}
}
printf("%d\n",slove(n,m,p));
}
return 0;
}