Working out CodeForces - 429B (四个角dp)

Working out

CodeForces - 429B

Summer is coming! It's time for Iahub and Iahubina to work out, as they both want to look hot at the beach. The gym where they go is a matrix a with n lines and m columns. Let number a[i][j] represents the calories burned by performing workout at the cell of gym in the i-th line and the j-th column.

Iahub starts with workout located at line 1 and column 1. He needs to finish with workout a[n][m]. After finishing workout a[i][j], he can go to workout a[i + 1][j] or a[i][j + 1]. Similarly, Iahubina starts with workout a[n][1] and she needs to finish with workout a[1][m]. After finishing workout from cell a[i][j], she goes to either a[i][j + 1] or a[i - 1][j].

There is one additional condition for their training. They have to meet in exactly one cell of gym. At that cell, none of them will work out. They will talk about fast exponentiation (pretty odd small talk) and then both of them will move to the next workout.

If a workout was done by either Iahub or Iahubina, it counts as total gain. Please plan a workout for Iahub and Iahubina such as total gain to be as big as possible. Note, that Iahub and Iahubina can perform workouts with different speed, so the number of cells that they use to reach meet cell may differs.

Input

The first line of the input contains two integers n and m (3 ≤ n, m ≤ 1000). Each of the next n lines contains m integers: j-th number from i-th line denotes element a[i][j] (0 ≤ a[i][j] ≤ 105).

Output

The output contains a single number — the maximum total gain possible.

Example
Input
3 3
100 100 100
100 1 100
100 100 100
Output
800
Note

Iahub will choose exercises a[1][1] → a[1][2] → a[2][2] → a[3][2] → a[3][3]. Iahubina will choose exercises a[3][1] → a[2][1] → a[2][2] → a[2][3] → a[1][3].

题意:两个人一个从左上角一个从左下角分别开始走分别走向右下角和右上角,(矩阵每个格子有数)问到达终点后可以得到的最大数是多少,并且条件是他们两个相遇的时候

那个点的数不能算

思路:首先这个题如果用搜索的话应该会超时时间复杂度大约是4^n,所以要用动态规划dp,怎么dp呢,首相会想,从起点两个角分别dp一下,递推式也比较好想到根据题目规定的方向

dp[i][j] = max(上一个方向最大的,上一个另一种方向最大的)+ 当前矩阵值。但是这样最终只能得到到达终点后不减去相交点的最大值,也就是全都加上了。

我们想,只是考虑左上角那个点的话起点记作start,假设在ij点处相遇,这个点记作now,然后继续走到终点右下角记作des,那么是不是这条路线的和就应该是start->now + now -> des这两段的数值的和,当然了不加now那个点,那我们再想,start->now这个点我们已经用dp求出来了,直接dp[i][j]就是当前的最大值,那么now->des是不是比较难求

其实now->des的总数值是不是就等于des->now的总数值,因此我们可以转化为求des->now的最大值,那么这个问题也就转化成了矩阵的四个角分别求dp,然后枚举每个点为相遇点,比较得出最大的即可

整体的做题思路就是这样的,但是在写代码的过程中会出现一个隐藏的非常深的极易容易犯的错误,将在代码注释中说明请看代码

code:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define MAXN 1019
int mp[MAXN][MAXN];
int dp1[MAXN][MAXN],dp2[MAXN][MAXN],dp3[MAXN][MAXN],dp4[MAXN][MAXN];
int n,m;
int main(){
    int i,j;
    cin >> n >> m;
    memset(mp,0,sizeof(mp));
    for(i = 1; i <= n; i++){
        for(j = 1; j <= m; j++){
            cin >> mp[i][j];
        }
    }
    //左上角求dp1
    memset(dp1,0,sizeof(dp1));
    for(i = 1; i <= n; i++){
        for(j = 1; j <= m; j++){
            dp1[i][j] = max(dp1[i-1][j],dp1[i][j-1]) + mp[i][j];
        }
    }
    //左下角求dp2
    memset(dp2,0,sizeof(dp2));
    for(i = n; i >= 1; i--){
        for(j = 1; j <= m; j++){
            dp2[i][j] = max(dp2[i+1][j],dp2[i][j-1]) + mp[i][j];
        }
    }
    //右上角求dp3
    memset(dp3,0,sizeof(dp3));
    for(i = 1; i <= n; i++){
        for(j = m; j >= 1; j--){
            dp3[i][j] = max(dp3[i-1][j],dp3[i][j+1]) + mp[i][j];
        }
    }
    //右下角求dp4
    memset(dp4,0,sizeof(dp4));
    for(i = n; i >= 1; i--){
        for(j = m; j >= 1; j--){
            dp4[i][j] = max(dp4[i+1][j],dp4[i][j+1]) + mp[i][j];
        }
    }
    //枚举每个相遇点求出总和,并比较选出最大值
    int maxn = -1;
    for(i = 2; i < n; i++){
        for(j = 2; j < m; j++){
            /**maxn = max(maxn,dp1[i][j]+dp2[i][j]+dp3[i][j]+dp4[i][j]-4*mp[i][j]);**/
            //注意注意!!上面这句话的写法是错误的,我一开始这样写测试点2一直过不去;最后终于想明白了为什么
            //因为可以行走的路线只有两种情况,正如下面的正确式子一样,因为对于两条路线(左上到右下和左下到右上),在相交点出,每条路线的出和入一定是相交
            //小正方形的对边,这是由题目规定的可行走方向决定的,不行你可以画个图试试,如果你让一条路线的入和出是相遇小正方形的临边,那么另一条路线将无论如何
            //都无法到达目的地,所以这也就是上面注释代码的错误之处,我们单纯的求当前dp[i][j]的最大值,却没有管他是从哪个方向过来的,而他们再i,j点的最大值很有可能
            //就是出入是临边,这也就是测试点2之所以过不去的原因,所以要分类考虑,如下:(只有两种情况)
            maxn = max(maxn,dp1[i-1][j]+dp2[i][j-1]+dp3[i][j+1]+dp4[i+1][j]);
            maxn = max(maxn,dp1[i][j-1]+dp2[i+1][j]+dp3[i-1][j]+dp4[i][j+1]);
        }
    }
    cout << maxn << endl;
    return 0;
}


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值