资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
有一个N x N的方格,每一个格子都有一些金币,只要站在格子里就能拿到里面的金币。你站在最左上角的格子里,每次可以从一个格子走到它右边或下边的格子里。请问如何走才能拿到最多的金币。
输入格式
第一行输入一个正整数n。
以下n行描述该方格。金币数保证是不超过1000的正整数。
输出格式
最多能拿金币数量。
样例输入
3
1 3 3
2 2 2
3 1 2
样例输出
11
数据规模和约定
n<=1000
一、题目分析
分析题目,N x N大小的方格,每个方格里面有一些金币,也就是一个二维数组checkered[n][n],其中checkered[i][j]的值代表该方格的金币数量,游戏规则是从左上角开始即(0,0)位置开始,只能往下走或者往右走,捡最多的金币。显而易见的动态规划问题,定义一个dp[n][n]数组,其中dp[i][j]就表示走到(i,j)位置时获取的最大金币数,除初位置之外,每个位置的最大金币数都是从上一个位置所得到的最大金币数加上该位置的金币数,从而到最后一个位置(n-1,n-1)的时候即为金币最多的时候。
二、解题思路
分析dp[n][n]数组中每个位置(i,j)中金币的最大值,直到最后一个位置即(n-1,n-1)的位置的值dp[n-1][n-1]即为题目所求的最大金币数。
1.首先看初始位置i = 0且j = 0时的情况:
这里不用走动,初始位置就是金币最大值的位置,dp[i][j] = checkered[i][j];
2.再看i = 0且j != 0时的情况:
因为题目只能往右或往下走,所以每个i = 0的位置(0,j)必定是从左边位置(0,j-1)所走过来的,从而可以得出该位置(0,j)的最大金币数dp[i][j] = dp[i][j-1] + checkered[i][j];
3.同样j = 0且i != 0时的情况:
因为题目只能往右或往下走,所以每个j = 0的位置(i,0)必定是由上边位置(i-1,0)所走过来的,从而可以得出该位置(i,0)的最大金币数dp[i][j] = dp[i-1][j] + checkered[i][j];
4.最后i != 0 且 j != 0时的情况:
由于在(i,j)位置是由上一个位置而走过来的,上一个位置只有两个来路,左边或者上边,我们只需要得出左边和上边中的最大值然后加上该位置的金币就是该位置的金币最大值了,可以得出dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + checkered[i][j];
三、代码编写
#include <iostream>
using namespace std;
int max(int a, int b) {
return a > b ? a : b;
}
int main() {
int n;
cin >> n;
/* 初始化方格 */
int checkered[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> checkered[i][j];
}
}
/* 求每个位置的最大金币数 */
int dp[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (i == 0 && j == 0) { //初始位置
dp[i][j] = checkered[i][j];
} else if (i == 0) { //从左边走过来的
dp[i][j] = dp[i][j-1] + checkered[i][j];
} else if (j == 0) { //从上边走过来的
dp[i][j] = dp[i-1][j] + checkered[i][j];
} else { //从左边或者上边走过来的
dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + checkered[i][j];
}
}
}
cout << dp[n-1][n-1];
return 0;
}
四、总结
这道题很适合初学动态规划算法的人来做,哈哈,因为我自己也就是刚学动态规划算法。