蓝桥杯---拿金币

  • 题目:

资源限制

内存限制: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]);
    }
}

 (有错误欢迎指正哈,如果有不小心侵权的联系删除哈😁) 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值