• 【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
    ",solve(n,m,p));
        }
        return 0;
    }//czy020202

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

  • 相关阅读:
    OpenStack local.conf
    Murano Weekly Meeting 2015.07.21
    Python package和folder
    WSGI学习系列eventlet.wsgi
    OpenStack Weekly Rank 2015.07.20
    Eventlet Greenlet
    OpenStack Weekly Meeting 2015.07.17
    OpenStack创建实例错误解决方法
    Linux Shell命令系列(5) VI编辑器
    linux统计使用最多的10个命令
  • 原文地址:https://www.cnblogs.com/Damitu/p/7654800.html
Copyright © 2020-2023  润新知