-
题目:
资源限制
内存限制:256.0MB C/C++时间限制:1.0s Java时间限制:3.0s Python时间限制:5.0s
问题描述
有一个N x N的方格,每一个格子都有一些金币,只要站在格子里就能拿到里面的金币。你站在最左上角的格子里,每次可以从一个格子走到它右边或下边的格子里。请问如何走才能拿到最多的金币。
输入格式
第一行输入一个正整数n。
以下n行描述该方格。金币数保证是不超过1000的正整数。输出格式
最多能拿金币数量。
样例输入
3
1 3 3
2 2 2
3 1 2样例输出
11
数据规模和约定
n<=1000
-
思路1:dfs(超时)
-
题目说从[0][0]开始,只能往下或往右走,即可以用dfs解决,我们设置一个max全局变量记录最多可以获取的金币数。
-
假设他先往下走,然后往下走的同时又会有两种选择,即往下和往右走(其实就像一颗树一样),每走一次将计算金币数的sum变量叠加上。
-
当往下走或者往右走任意一个方向走到尽头时,我们认为这条路已经走完了,这时就用一路走下来的累加的金币数sum和全局变量max比较,用max存储最多金币数。
-
然后回溯回去,上一步在往另外一个方向走,然后重复第二步,直到所有方向都走完为止,此时得max即为最多得金币数。
-
但是!我们往回看数据规模,n最大可以到1000,这种dfs递归得方法时必然超时的,代码提交以后显然也只完成了第一个样例!
-
-
代码1:
package BlueBridge;
import java.util.Scanner;
public class TakeGoldCoins {
public static int max = Integer.MIN_VALUE;
public static int[][] nums;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
nums = new int[n][n];
for (int i = 0; i < nums.length; i++) {
for (int j = 0; j < nums.length; j++) {
nums[i][j] = in.nextInt();
}
}
dfs_method();
// dp_method();
}
private static void dfs_method() {
int sum = nums[0][0];
dfs('d', 0, 0, sum);
dfs('r', 0, 0, sum);
System.out.println(max);
}
//direction是前进方向,r右d下,i,j为目前所在的位置,sum为累加的金币数
public static void dfs(char direction, int i, int j, int sum) {
if (direction == 'd') {
if (i + 1 >= nums.length) { //往下走走到了尽头。此时我们认为这条路已经走完
max = Math.max(sum, max); //将他的累计金币数和最大金币数max比较,取最大
return; // 退出这条路
}
sum = sum + nums[i + 1][j]; //若还能走,就累加上此时的金币数
i = i + 1; //定位此时的位置
} else if (direction == 'r') { //同上
if (j + 1 >= nums.length) {
max = Math.max(sum, max);
return;
}
sum = sum + nums[i][j + 1];
j = j + 1;
}
dfs('d', i, j, sum); //往下走
dfs('r', i, j, sum); //往右走
}
}
-
思路2:dp(题目给的标签也是dp)
-
参考了其他博主的想法,发现dp解这题是最优的(85条消息) 蓝桥杯算法训练-拿金币_我爱让机器学习的博客-CSDN博客
-
dp的话就是寻找最优的解,设定一个最优矩阵dp,然后计算每个格子的最优情况。
-
这里再往下走的时候有三种情况,第一种是初始状态下,[0][0]时就直接收掉此时的金币即可。
-
第二种情况是当所处位置靠边时(即最靠左j=0或最靠上i=0),只能往下或往右走(因为我们当前的位置为ij,上一个位置我们定义为i-1或j-1,如果不这样走会index out)。
-
第三种情况是ij都不靠边,此时走就是斜着走(分解就是先右后下或先下后右),此时就有一个最优解的选取了,就是选max(右,下)+当前的[i][j]
-
要拿最多的金币,走的路也应该是最多,所以我们最后锁定最多金币的是右下角的[n-1][n-1]位置。
-
-
代码2:
package BlueBridge;
import java.util.Scanner;
public class TakeGoldCoins {
public static int[][] nums;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
nums = new int[n][n];
for (int i = 0; i < nums.length; i++) {
for (int j = 0; j < nums.length; j++) {
nums[i][j] = in.nextInt();
}
}
// dfs_method();
dp_method();
}
private static void dp_method() {
int[][] dp = new int[nums.length][nums.length]; //记录到某个格子为止的金币数
for (int i = 0; i < nums.length; i++) {
for (int j = 0; j < nums.length; j++) {
if (i == 0 && j == 0) //当在左上角就不用走,直接收掉
dp[i][j] = nums[i][j];
else if (i != 0 && j == 0) //若靠边的(靠上或靠左)此时只能往右或往上走
dp[i][j] = dp[i - 1][j] + nums[i][j];
else if (i == 0 && j != 0)
dp[i][j] = dp[i][j - 1] + nums[i][j];
else //不是靠边的话有两种走法到达这一格,往下右或往右下,这个时候就有选择了,选最大的
dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]) + nums[i][j];
}
}
System.out.println(dp[nums.length-1][nums.length-1]);
}
}
(有错误欢迎指正哈,如果有不小心侵权的联系删除哈😁)