• LeetCode 155. 掷骰子等于目标和的方法数:动态规划


    【LetMeFly】1155.掷骰子等于目标和的方法数:动态规划

    力扣题目链接:https://leetcode.cn/problems/number-of-dice-rolls-with-target-sum/

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

    给定三个整数 nk 和 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

    方法一:动态规划(DP)

    开辟一个动态规划数组 d p dp dp,其中 d p [ i ] [ j ] dp[i][j] dp[i][j]代表 i i i个骰子的和为 j j j的方案数。

    初始值 d p [ i ] [ j ] = 0 dp[i][j]=0 dp[i][j]=0,而 d p [ 1 ] [ 1 − k ] = 1 dp[1][1-k]=1 dp[1][1k]=1

    这样,我们就可以从第二天开始枚举:

    for i from 2 to n:  # i个骰子
       for j from 1 to target:  # 和为j
           for _k from 1 to min(k, target):  # i个骰子和为j,可以由 i-1个骰子和为j-_k 加上 一个值为_k的骰子 得到
    	       dp[i][j] = (dp[i][j] + dp[i - 1][j - _k]) % MOD
    
    • 1
    • 2
    • 3
    • 4

    优化:

    1. 不难发现 i i i个骰子的状态只和 i − 1 i-1 i1个骰子的状态有关,因此可以将二维数组压缩为一维。
    2. 我们初始化了1个骰子从1到k的方案数为1,其实我们也可以只领 d p [ 0 ] [ 0 ] = 1 dp[0][0]=1 dp[0][0]=1(0个骰子和为0的方案数为1)

    复杂的分析

    • 时间复杂度 O ( n × k × t a r g e t ) O(n\times k\times target) O(n×k×target)
    • 空间复杂度 O ( n × t a r g e t ) O(n\times target) O(n×target) O ( t a r g e t ) O(target) O(target)

    AC代码

    C++

    没有进行空间优化:

    typedef long long ll;
    const ll MOD = 1e9 + 7;
    class Solution {
    public:
        int numRollsToTarget(int n, int k, int target) {
            vector<vector<ll>> dp(n + 1, vector<ll>(target + 1, 0));
            for (int j = 1; j <= min(k, target); j++) {
                dp[1][j] = 1;
            }
            for (int i = 2; i <= n; i++) {
                for (int j = 1; j <= target; j++) {
                    for (int _k = 1; _k <= min(k, j); _k++) {
                        dp[i][j] = (dp[i][j] + dp[i - 1][j - _k]) % MOD;
                    }
                }
            }
            return dp[n][target];
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    Python

    进行了空间优化:

    MOD = int(1e9 + 7)
    class Solution:
        def numRollsToTarget(self, n: int, k: int, target: int) -> int:
            dp = [1] + [0] * target
            for i in range(1, n + 1):
                for j in range(target, -1, -1):
                    dp[j] = 0
                    for _k in range(1, min(k + 1, j + 1)):
                        dp[j] = (dp[j] + dp[j - _k]) % MOD
            return dp[-1]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    同步发文于CSDN,原创不易,转载经作者同意后请附上原文链接哦~
    Tisfy:https://letmefly.blog.csdn.net/article/details/134023955

  • 相关阅读:
    flink-sql所有表连接器-1.14
    SpringBoot自动装配详解_回顾一下2
    OpenResty安装-(基于Nginx的高性能Web平台,可在Nginx端编码业务)
    PowerBI 首购用户分析(Tableau经典案例复刻)
    docker 安装nacos
    彻底弄懂C/C++指针数组与数组指针
    【数据结构】&&【C++】平衡搜索二叉树的模拟实现(AVL树)
    服务器系统和普通系统的区别
    关于Elasticsearch的自动补全、数据同步和集群,以下是相关的知识点
    ES6箭头函数
  • 原文地址:https://blog.csdn.net/Tisfy/article/details/134023955