[leetcode 周赛 149] 1155 掷骰子的N种方法

1155 Number of Dice Rolls With Target Sum 掷骰子的N种方法

描述

这里有 d 个一样的骰子,每个骰子上都有 f 个面,分别标号为 1, 2, ..., f
我们约定:掷骰子的得到总点数为各骰子面朝上的数字的总和
如果需要掷出的总点数为 target,请你计算出有多少种不同的组合情况(所有的组合情况总共有f^d 种),模10^9 + 7后返回。

  • 示例 1:

输入:d = 1, f = 6, target = 3
输出:1

  • 示例 2:

输入:d = 2, f = 6, target = 7
输出:6

  • 示例 3:

输入:d = 2, f = 5, target = 10
输出:1

  • 示例 4:

输入:d = 1, f = 2, target = 3
输出:0

  • 示例 5:

输入:d = 30, f = 30, target = 500
输出:222616187

  • 提示:
    1 <= d, f <= 30
    1 <= target <= 1000

思路

这明显是个动态规划问题

  • 规律公式

d为骰子数, f为骰子面数, target为目标值, k表示当前第d个骰子掷出的数

  • 初始状态

需要投掷的目标数在单个骰子的范围内 返回1, 否则返回0

代码实现

递归实现

class Solution {
    static int MOD = (int)(1e9+7);
    
    public int numRollsToTarget(int d, int f, int target) {
        if (target < d | target > d*f) return 0;
        
        int[][] dp = new int[d+1][target+1];
        // 初始化 全-1 表示未被处理
        for (int[] dpi : dp) Arrays.fill(dpi, -1);
        
        return helper(d, f, target, dp);
    }
    
    int helper(int d, int f, int target, int[][] dp) {
        // target 超出骰子的投掷范围
        if (target < d | target > d*f) return 0;
        // 如果已经处理过 使用之前记录数据
        if (dp[d][target] != -1) return dp[d][target];
        // 当单个骰子时
        if (d==1) {
            if (target <= f && target > 0) return dp[d][target]=1;
            else return dp[d][target]=0;
        }
        // 数据没有被记录 骰子数大于等于2
        int ans = 0;
        // 对当前骰子投掷出的每个面数都进行处理
        for (int cur=1; cur <= f; cur++) {
            ans = (ans + helper(d-1, f, target-cur, dp))%MOD;
        }
        // 对数据进行记录
        return dp[d][target]=ans;
    }
}

非递归实现

class Solution {
    // 取模数 1e9 + 7
    final static int MOD = 1000000007;
    public int numRollsToTarget(int d, int f, int target) {
        if (target < d | target > d*f) return 0;
        
        // dp 表示当骰子为i(1~d)时j(1~target)的组合数
        // 其实长度可以是2 因为只使用上一轮的数据
        int[][] dp = new int[d+1][target+1];
        
        // 初始为1
        dp[0][0] = 1;
        // 从有1个骰子开始 1~d
        for (int i = 1; i <= d; i++) {
            // 当前骰子投掷数 1~f
            for (int cur = 1; cur <= f; cur++) {
                // 加上上一轮没有投掷cur时prev的组合数
                for (int prev = 0; prev+cur <= target; prev++) {
                    dp[i][prev+cur] = (dp[i][prev+cur] + dp[i-1][prev])%MOD;
                }
            }
        }
        
        return dp[d][target];
    }
}
原文地址:https://www.cnblogs.com/slowbirdoflsh/p/11366818.html