【每日一题】1155. 掷骰子等于目标和的方法数-2023.10.24

题目:

1155. 掷骰子等于目标和的方法数

这里有 n 个一样的骰子,每个骰子上都有 k 个面,分别标号为 1 到 k 。

给定三个整数 n ,  k 和 target ,返回可能的方式(从总共 kn 种方式中)滚动骰子的数量,使正面朝上的数字之和等于 target 。

答案可能很大,你需要对 109 + 7 取模 。

示例 1:

输入:n = 1, k = 6, target = 3
输出:1
解释:你扔一个有 6 个面的骰子。
得到 3 的和只有一种方法。

示例 2:

输入:n = 2, k = 6, target = 7
输出:6
解释:你扔两个骰子,每个骰子有 6 个面。
得到 7 的和有 6 种方法:1+6 2+5 3+4 4+3 5+2 6+1。

示例 3:

输入:n = 30, k = 30, target = 500
输出:222616187
解释:返回的结果必须是对 109 + 7 取模。

提示:

  • 1 <= n, k <= 30
  • 1 <= target <= 1000

解答:

这里有 n 个一样的骰子,每个骰子上都有 k 个面,分别标号为 1 到 k 。

给定三个整数 n , k 和 target ,返回可能的方式(从总共 k^n 种方式中)滚动骰子的数量,使正面朝上的数字之和等于 target 。

答案可能很大,你需要对 10^9 + 7 取模 。

题目分析
这道题要求的是 n 个 取值范围为 [1, k] 的骰子和为 target 的情况数。 如果说我们逐一枚举的话,那么时间复杂度就是所有的情况数 k^n 。相比于这种幂次级的时间复杂度,我们即使将复杂度降为 n^3 都是有很大的效果。

我们换个角度想,假设我第 n 个骰子的值就固定为某个值 j,那么 n 个骰子的和要为 target,那么前 n-1 个骰子的和就只能为 target - j。因此n 个骰子的和要为 target的情况数就取决于前 n-1 个骰子的和为 target - j 的情况数。 而j 的取值范围为 [1, k],这里可以凝练出一个状态转移式:

其中 f(n, target) 表示n 个骰子的和要为 target的情况数。由于骰子的值恒为正,则和必然为正,因此这里有一个限定条件,target - j >= 0【为什么和有 0 的情况我们后面说】

既然我们将n 个骰子的和要为 target的情况数的问题 转换为 前 n-1 个骰子的和为 target - j 的情况数的问题。那么同理,假设我第 n-1 个骰子的值就固定为某个值 b,那么那么前 n-2 个骰子的和就只能为 target - j - b,这个问题其实前一个问题的一个子问题,满足动态规划的条件,并且有状态转移方程:

其中 dp[i][s] 表示 i 个骰子的和要为 s的情况数。最终的结果就为 dp[n][target]。这里我们看到 i 的取值范围为 [1, n]【骰子数】,s的取值范围为 [1, target]【和的情况,我们要找目标是target,因此更大的数不再考虑范围内】,因此:

i=0,s > 0:表示 0个骰子的和为一个正数,不可能,情况皆为 0;
s=0,i > 0:表示 i个骰子的和为 0,不可能,情况皆为 0;
i=0,s = 0:表示 0个骰子的和为 0,这就是初始状态,情况为 1;【这也是为什么上面 target-a可以取等于零。因此在0个骰子的时候就有和为零的情况】

代码:
 

class Solution {
    static final int MOD=1000000007;//模值
    public int numRollsToTarget(int n, int k, int target) {
        int[][] dp=new int[n+1][target+1];//dp[i][j]表示i个筛子和为j的情况数
        dp[0][0]=1;//dp[0][0]初始为1,表示0个骰子和为0的情况只有一种
        for(int i=1;i<=n;i++){  //依次枚举骰子数
            for(int s=1;s<=target;s++){//依次枚举和的情况,因为所有值都为正,因此只需要找到target即可
                for(int j=1;j<=k;j++){//枚举第i个骰子的值的情况
                    if(s-j<0) continue;//值大于和,这个值不可能参与到构成和s中
                    dp[i][s]+=dp[i-1][s-j];//状态转移
                    dp[i][s]%=MOD;
                }
            }
        }
        return dp[n][target];//表示n个骰子和为target的情况数
    }
}

结果:

【每日一题】1155. 掷骰子等于目标和的方法数-2023.10.24_第1张图片

你可能感兴趣的:(leetcode刷题笔记,算法,数据结构,leetcode)