【CZY选讲·最大子矩阵和】
题目描述
有一个n*m的矩阵,恰好改变其中一个数变成给定的常数P,使得改变后的这个矩阵的最大子矩阵最大。
数据范围
n,m<=300。
题解:
①如果没有p,那么二维矩阵和就是一维最长连续子序列的DP升级就可以了:
设f[i][j][k]表示在i行j行之间1~k列这一个矩形中的最大子矩阵的值
转移方程:f[i][j][k]=max(f[i][j][k-1]+sum[k],sum[k])
其中sum[k]表示(i,k)-(j,k)这一段一维序列的元素和。
上述在代码实现的时候可以压维,即覆盖以前的答案。
②根据题意,加一维[1/0]表示到目前为止最优矩阵中有没有点被更改了:
然后转移同理,只是如果选择修改,肯定是修改最小的数,所以使用RMQ或者暴力得出即可
#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; }//czy020202
我只想朝着远方边走边唱,歌唱这生命美丽和迷惘。————汪峰《边走边唱》