计蒜客 CSP-J2 2020

  D. 方格取数【民间数据】

  •  1000ms
  •  262144K

设有 n×m 的方格图,每个方格中都有一个整数。现有一只小熊,想从图的左上角走到右下角,每一步只能向上、向下或向右走一格,并且不能重复经过已经走过的方格,也不能走出边界。小熊会取走所有经过的方格中的整数,求它能取到的整数之和的最大值。

输入格式

输入文件名为 number.in

第 1 行两个正整数 n,m。

接下来 n 行每行 m 个整数,依次代表每个方格中的整数。

输出格式

输入文件名为 number.out

一个整数,表示小熊能取到的整数之和的最大值。

数据范围与提示

对于 2020% 的数据,n,m≤5。

对于 4040% 的数据,n,m≤50。

对于 7070% 的数据,n,m \le 300n,m≤300。

对于 100100% 的数据,1≤n,m≤1000。方格中整数的绝对值不超过 10^4。

输出时每行末尾的多余空格,不影响答案正确性

要求使用「文件输入输出」的方式解题,输入文件为 number.in,输出文件为 number.out

样例输入1

3 4
1 -1 3 2
2 -1 4 -1
-2 2 -3 -1

样例输出1

9

样例解释1

image.png

按上述走法,取到的数之和为 1+2+(-1)+4+3+2+(-1)+(-1)=91+2+(−1)+4+3+2+(−1)+(−1)=9,可以证明为最大值。

image.png

注意,上述走法是错误的,因为第 2 行第 2 列的方格走过了两次,而根据题意,不能重复经过已经走过的方格。

image.png

另外,上述走法也是错误的,因为没有走到右下角的终点。

样例输入2复制

2 5
-1 -1 -3 -2 -7
-2 -1 -4 -1 -2

样例输出2复制

-10

样例解释2

image.png

按上述走法,取到的数之和为 (-1)+(-1)+(-3)+(-2)+(-1)+(-2)=-10(−1)+(−1)+(−3)+(−2)+(−1)+(−2)=−10,可以证明为最大值。因此,请注意,取到的数之和的最大值也可能是负数。

思路:其实这题可以用DP来解,主要问题就是状态表示。如果我们把所有的格子割裂开来看,那么十分难以表示,首先,题目要求走过的格子不能再走一次,因此我们可以增加一个维度表示当前格子是从哪一个格子转移过来的,但是,对于两个上下邻接的格子,我们是很难固定一种遍历方式去遍历整个数组的。换种思路,其实本身题目要求行走方式为上、下、右,是绝对不会向左走的,我们是否可以强制让格子只向右走呢?答案是否定的!但是,如果我们一列一列考虑的话,实际上我们可以强制当前列的所有格子下一步都必须向右走,这样我们将题目变成:路线为在同列中上下移动,在列间只能右移动,这样的好处是,我们在考虑点(i,j)的时候,只需要考虑他从(1~n,j)和(i,j-1)转移过来的最大值就好。于是我们用dp[i][j]表示到达(i,j)时的最大值,当我们要计算第j+1列的时候,我们让我们利用j列的结果进行扩展就好了。具体来说,我们考虑使用(i,j)号点对第j+1列上的格子进行扩展的话,那么此时(i,j+1)的值就是dp[i][j]+Num[i][j+1],然后用这个值往上往下直线走就好啦,最后第j+1列的每一个格子都会被第j列的n个格子扩展一遍,取最大的即可。

#include<bits/stdc++.h>
using namespace std;

const int MaxN = 1010;
typedef long long LL;

LL Num[MaxN][MaxN];
LL dp[MaxN][MaxN], help[MaxN];
bool visited[MaxN];

int main(){
    ifstream fin("number.in");
    ofstream fout("number.out");
    LL n, m, i, j, k;
    fin >> n >> m;
    for(i = 1; i <= n; ++ i){
        for(j = 1; j <= m; ++ j){
            fin >> Num[i][j];
            dp[i][j] = -1000000000000;
        }
    }
    for(i = 1; i <= n; ++ i){
        dp[i][1] = dp[i - 1][1] + Num[i][1];
    }
    for(j = 2; j <= m; ++ j){ // 扩展前m-1列
        for(i = 1; i <=n; ++ i){
            help[i] = dp[i][j - 1] + Num[i][j]; // 从 i,j 扩展
            for(k = i - 1; k >= 1; -- k){
                help[k] = help[k + 1] + Num[k][j];
            }
            for(k = i + 1; k <= n; ++ k){
                help[k] = help[k - 1] + Num[k][j];
            }
            for(k = 1; k <= n; ++ k) dp[k][j] = max(dp[k][j], help[k]);
        }
    }
    fout << dp[n][m];
    return 0;
} 

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值