题目链接:hihocoder 1580
题意:给出n*m矩阵和p,要求修改其中一点的值为输入的p,再求最大子矩阵。
思路:参考http://blog.youkuaiyun.com/luricheng/article/details/78074046
我们考虑没有p时,求最大子矩阵和:
对sum[i]求一遍最大字段和即可得到最大子矩阵(i表示第i列),O(n^3)。
当要求修改其中一个值为p,再求最大子矩阵和,分两种情况:
首先,不管哪种情况,要换我们肯定选最小的换,所以记录每一个字段和(一列中的某一段的和)的最小值。minval[k]=min(mat[x][k]|i<=x<=j)。同时,sum[i]也必须要有。
情况①:最大子矩阵的规模是n*m,说明修改在最大子矩阵中发生了 O(n^2)
情况②:最大子矩阵的规模不是n*m,说明修改可发生在最大子矩阵内或者外,关键选最优。O(n^3)
这个明白之后,关键还是状态的定义和状态转移,看代码就能懂:
d[i][0]=以第i列为终点,未修改过某点为p的最大子矩阵和值
d[i][1]=以第i列为终点,修改过某点为p的最大子矩阵和值
dp[i][0] = max(dp[i-1][0],0)+sum[i];
dp[i][1] = max(dp[i-1][1] + sum[i],max(dp[i-1][0],0) + sum[i] - minval[i] + p);
#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#define Max(a,b) a>b?a:b
#define Min(a,b) a>b?b:a
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
int dir[4][2]= {{1,0},{-1,0},{0,1},{0,-1}};
const double eps = 1e-6;
const double Pi = acos(-1.0);
const int INF=0x3f3f3f3f;
const int maxn = 310;
int mat[maxn][maxn];
int minval[maxn];
int sum[maxn];
int dp[maxn][2];
int dp1(int* sum,int m,int p){
int ret = -INF;
for(int i = 0; i < m; i++){
int minn = INF;
int summ = 0;
for(int j = i; j < m; j++){
summ += sum[j];
minn = min(minn,minval[j]);
if(i == 0 && j == m-1){
ret = max(ret,summ - minn + p);
}else{
int maxx = max(summ,summ - minn + p);
ret = max(maxx,ret);
}
}
}
return ret;
}
int dp2(int* sum,int m,int p){
dp[0][0] = sum[0];
dp[0][1] = sum[0] - minval[0] +p;
for(int i = 1; i < m; i++){
dp[i][0] = max(dp[i-1][0],0)+sum[i];
dp[i][1] = max(dp[i-1][1] + sum[i],max(dp[i-1][0],0) + sum[i] - minval[i] + p);
}
int ret = -INF;
for(int i = 0; i < m; i++){
ret = max(ret,max(dp[i][0],dp[i][1]));
}
return ret;
}
int solve(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,dp1(sum,m,p));
}else{
ans = max(ans,dp2(sum,m,p));
}
}
}
return ans;
}
int main(){
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",solve(n,m,p));
}
return 0;
}