题目描述
设有n×m的方格图,每个方格中都有一个整数。现有一只小熊,想从图的左上角走到右下角,每一步只能向上、向下或向右走一格,并且不能重复经过已经走过的方格,也不能走出边界。小熊会取走所有经过的方格中的整数,求它能取到的整数之和的最大值。
输入
输入文件名为 number.in。
第 1 行两个正整数n,m。
接下来n行每行m个整数,依次代表每个方格中的整数。
输出
输入文件名为 number.out。
一个整数,表示小熊能取到的整数之和的最大值
样例输入1
3 4
1 -1 3 2
2 -1 4 -1
-2 2 -3 -1
样例输出1
9
提示/说明
【样例1解释】
按上述走法,取到的数之和为 1 + 2 + (-1) + 4 + 3 + 2 + (-1) + (-1) = 9,可以证明为最大值。
注意,上述走法是错误的,因为第 2 行第 2 列的方格走过了两次,而根据题意,不能重复经过已经走过的方格。
另外,上述走法也是错误的,因为没有走到右下角的终点。
【样例2解释】
按上述走法,取到的数之和为(-1) + (-1) + (-3) + (-2) + (-1) + (-2) = -10,可以证明为最大值。因此,请注意,取到的数之和的最大值也可能是负数。
【样例 3】见选手目录下的 number/number3.in 与 number/number3.ans。
【数据范围与提示】
对于 20% 的数据,n,m≤ 5。
对于40% 的数据,n,m≤ 50。
对于 70% 的数据,n,m≤ 300。
对于 100% 的数据,1 ≤n,m≤ 1000。方格中整数的绝对值不超过 10^4。
这题刚一看:不就是个普通的dp吗
自信写代码ing
诶,这怎么还带往上走的?
😓😓😓😓
这题与经典dp差别就在这里
根据dp的无后效性,不能往上走又往下走
思路:
一次dp不行,那就两次(
定义f数组时发生变化
之前的二维不得不提升维度变成三维(蒟蒻目前想不出这题的二维AC方法)
第三维:
f[i][j][0]表示从上面那个点或者右边的点过来的最大值
画个丑图~
f[i][j][0]=max(f[i-1][j][0],max(f[i][j-1][0],f[i][j-1][1]))+a[i][j];
(因为无有效性,如果这个点是从上面的点过来的话,那么上面的点的第三维也得是0,下面同理)
很好理解
f[i][j][1]代表从右边或者下边过来的最大值
f[i][j][1]=max(f[i+1][j][1],max(f[i][j-1][0],f[i][j-1][1]))+a[i][j];
初始化 !!!
这题的初始化不能小看,也很特殊
首先先要memset一下毋庸置疑
可是把f数组弄成几呢
0x3f?
-1?
0?
nononononononono
因为代码中有max,如果弄成无限大,那就乱了
所以得定义成无限小:0xcf
然后f[1][0][0]=0 因为我们目前不知道f[1][0][1]是什么,毕竟后面会计算,所以不能初始化f[1][0][1]
上AC代码了~
# include <iostream>
# include <cstdio>
# include <cstring>
using namespace std;
# define int long long
# define N 1005
int n,m;
int a[N][N],f[N][N][2];
signed main(){
scanf("%lld%lld",&n,&m);
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
scanf("%lld",&a[i][j]);
}
}
memset(f,0xcf,sizeof(f));
f[1][0][0]=0;
for (int j=1;j<=m;j++){//因为上下走是按列的,所以外层循环是j
for (int i=1;i<=n;i++){
f[i][j][0]=max(f[i-1][j][0],max(f[i][j-1][0],f[i][j-1][1]))+a[i][j];
}
for (int i=n;i;i--){
f[i][j][1]=max(f[i+1][j][1],max(f[i][j-1][0],f[i][j-1][1]))+a[i][j];
}
}
printf("%lld",f[n][m][0]);//因为右下角的点不能从下面走来,所以得输出上面走来的
return 0;
}
AC记录
制作不易,点个赞吧~