2017 ACM-ICPC 亚洲区(北京赛区)网络赛C.Matrix (DP)

本文介绍了一种解决矩阵最大子矩阵和问题的方法,通过动态规划实现矩阵中某点值的更改并求解修改后的最大子矩阵和。文章详细解释了状态定义与状态转移过程,并提供了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接: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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值