zoukankan      html  css  js  c++  java
  • [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];
        }
    }
    
  • 相关阅读:
    韩寒做错了(update 4 12)。
    放弃IE6。
    阿弥陀佛,我没有“抄袭”。
    婚姻。
    爆牙齿饭否?
    地震之后——和妈妈对话。
    8年前,《西班牙,我为你哭泣。》
    在等决赛中提问。
    地震之后——中国互联网在黑夜中哭泣。
    年轻。
  • 原文地址:https://www.cnblogs.com/slowbirdoflsh/p/11366818.html
Copyright © 2011-2022 走看看