【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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

我只想朝着远方边走边唱,歌唱这生命美丽和迷惘。————汪峰《边走边唱》

posted @ 2017-10-12 10:11  小米狐  阅读(1407)  评论(0编辑  收藏  举报
TOP