【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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

我只想朝着遠方邊走邊唱,歌唱這生命美麗和迷惘。————汪峰《邊走邊唱》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM