小M的弹子游戏机挑战题解 | 豆包MarsCode AI刷题

152 阅读4分钟

题目详情

小M最近迷上了一款弹子游戏机,规则如下:
玩家可以在版面最上方任意一个位置放置弹珠。弹珠会通过得分点时为玩家赢得分数,目标是获得尽可能高的分数。
弹子游戏机的版面由两种组成要素构成:

  1. 钉子(用 -1 表示),当弹珠碰到钉子时,有可能弹射到左下或者右下的位置。
  2. 得分点(非负整数),弹珠经过得分点时可以获得对应的分数。

如果弹珠所在的格子为空(即没有钉子或者得分点),弹珠会直接往下落。

小M想知道,在一个给定的版面布局中,他能够获得的最高分数是多少。

  • n 表示版面的高度。
  • m 表示版面的宽度。
  • array 是一个 n x m 的二维数组,其中:
  • -1 表示该位置为钉子;
  • 0 表示该位置为空;
  • 正整数表示该位置为得分点,值为该得分点的分数。

解决方案

解法1 递归实现

本题每一轮的决策在于三个位置的状态变化。左,中,右 有分数,求最大分数s 设状态[i,j]

  1. [i,j]=0 [i+1,j]
  2. [i,j]=-1 [i+1,j-1] (要求j>0) or [i+1,j+1] (要求j<array[i].length-1)

若使用递归实现

伪代码为

i=array.length return array[i,j]
if(array[i,j]==0){sol(i+1,j)}
if(array[i,j]==-1)
if(j==0){sol(i+1,j+1)}
else if(j==array[i].length-1){sol(i+1,j-1)}
else{max(sol(i+1,j-1),sol(i+1,j+1))}
return max(maxv,上面接口)

代码实现:

public class Main {
    public static int solution(int n, int m, int[][] array) {
        // Edit your code here
        int maxv = -1;
        for (int j = 0; j < m; j++) {
            maxv = Math.max(maxv, sol(0, j, array));
        }
        return maxv;
    }

    private static int sol(int i, int j, int[][] array) {
        if (i == array.length - 1) {
            return rcheck(array[i][j]);
        }
        if (array[i][j] == 0) {
            return sol(i + 1, j, array);
        } else if (array[i][j] == -1) {
            if (j == 0) {
                return rcheck(array[i][j]) + sol(i + 1, j + 1, array);
            } else if (j == array[i].length - 1) {
                return rcheck(array[i][j]) + sol(i + 1, j - 1, array);
            } else {
                return rcheck(array[i][j]) + Math.max(sol(i + 1, j - 1, array), sol(i + 1, j + 1, array));
            }
        } else {
            return array[i][j] + sol(i + 1, j, array);
        }

    }

    private static int rcheck(int n) {
        if (n == -1) {
            return 0;
        } else {
            return n;
        }
    }

    public static void main(String[] args) {
        // Add your test cases here
        System.out.println(solution(3, 3, new int[][] { { -1, 0, -1 }, { 100, 0, 0 }, { 0, 50, 70 } }) == 50);
        System.out.println(
                solution(4, 3, new int[][] { { -1, 0, -1 }, { 0, -1, 0 }, { 50, 100, 70 }, { 80, 200, 50 } }) == 130);
    }
}

复杂度分析:

时间复杂度
  1. 递归调用次数

    • 在最坏情况下,每次递归调用会分裂成两个子问题(当遇到钉子时)。
    • 递归树的深度为 n(版面的高度)。
    • 因此,递归树的节点数大约为 2^n
  2. 每个节点的操作

    • 每个节点的操作是常数时间(如比较、加法等)。

综合以上分析,递归算法的时间复杂度为 O(2^n)

解法2 动态规划实现

这题最好的办法是使用动态规划实现。
初始化:

创建一个与版面布局相同大小的二维数组 dp,用于存储从每个位置开始的最大得分。

状态转移:

从最底层开始向上逐层计算每个位置的最大得分。 对于每个位置 (i, j): 如果当前位置是得分点,则 dp[i][j] 为当前得分加上下一层对应位置的最大得分。 如果当前位置是钉子,则 dp[i][j] 为下一层左下和右下位置的最大得分中的较大值。 如果当前位置是空位,则 dp[i][j] 为下一层对应位置的最大得分。

最终结果:

最上层的 dp[0][j] 中的最大值即为所求的最大得分。

动态规划实现代码:

public class Main {
    public static int solution(int n, int m, int[][] array) {
        // Edit your code here
        int dp[][] = new int[n][m];
        int max = -1;
        for (int i = 0; i < m; i++) {
            dp[n - 1][i] = checkt(array[n - 1][i]);
        }
        for (int i = n - 2; i >= 0; i--) {
            for (int j = 0; j < m; j++) {
                if (array[i][j] == -1) {
                    if (j == 0) {
                        dp[i][j] = dp[i + 1][j + 1];
                    } else if (j == m - 1) {
                        dp[i][j] = dp[i + 1][j - 1];
                    } else {
                        dp[i][j] = Math.max(dp[i + 1][j + 1], dp[i + 1][j - 1]);
                    }
                } else if (array[i][j] == 0) {
                    dp[i][j] = dp[i + 1][j];
                } else {
                    dp[i][j] = array[i][j] + dp[i + 1][j];
                }
            }
        }
        for (int i = 0; i < m; i++) {
            max = Math.max(max, dp[0][i]);
        }
        return max;
    }

    private static int checkt(int n) {
        if (n == -1) {
            return 0;
        }
        return n;
    }

    public static void main(String[] args) {
        // Add your test cases here
        System.out.println(solution(3, 3, new int[][] { { -1, 0, -1 }, { 100, 0, 0 }, { 0, 50, 70 } }) == 50);
        System.out.println(
                solution(4, 3, new int[][] { { -1, 0, -1 }, { 0, -1, 0 }, { 50, 100, 70 }, { 80, 200, 50 } }) == 130);
    }
}
时间复杂度
  1. 初始化

    • 初始化 dp 数组的最底层需要遍历 m 个元素,时间复杂度为 O(m)
  2. 状态转移

    • 对于每一层 i,需要遍历 m 个元素,并对每个元素进行常数时间的操作(如赋值、比较、加法等)。
    • 总共有 n 层,因此状态转移的总时间复杂度为 O(n * m)
  3. 最终结果

    • 在最上层遍历 dp[0][i] 并找到最大值,时间复杂度为 O(m)

故动态规划算法的时间复杂度为 O(n * m)