leetcode 1155.掷骰子的N种方法

原题如下

https://leetcode-cn.com/problems/number-of-dice-rolls-with-target-sum/
在这里插入图片描述

题解

方法一 DP+卷积

首先从简单的地方来说,从常规的色子讲起吧,最普通的正方体色子。
假如色子就是我们日常看到的六面的色子,那么当只有一枚色子的时候,我们掷出1到6点数的组合数都是1(也就是此时的点数和种数的数组可以表示为长度为6,且每个元素都是1的数组,这个数组暂且记为yige[],yige.length=6,并且yige[i]=1),当我们增加一枚色子(也就是此时有两枚色子,当然可以掷出的点数和也是相应的从1到6换到了2到12),我们发现此时掷出的点数和可以从某些掷一枚色子的时候的情况推导而来。例如两枚色子我们掷出8点,可以是第一枚色子2点,第二枚6点,或者第一枚3点,第二枚5点,以及(4,4)、(5,3)、(6,2),简而言之,就是一些数组元素相加,即是yige[1]+yige[2]+yige[3]+yige[4]+yige[5]。
看到这里,我们可以知道,当有两枚色子的时候,我们可以根据想要掷出的点数,对应地从一枚色子的时候的结果中选择可以作为第一枚色子点数的情况的所对应的种数来简单相加(这里所说的“可以作为第一枚色子点数的情况”是因为有些情况是无法作为组合的,比如说我们要得到10的点数,那么就不会出现(7,3)这样的组合,或者说第一枚点数是3的时候,你拿的两枚色子的和无论如何也得不到2,也就是要看好色子点数的范围).
知道了这个递推的规律,我们就需要简单了解卷积了,其实卷积这是个比较复杂的概念,在信号的处理里面涉及的比较多,卷积在这里我就提一下把(其实得到d个色子的对应数组就是yige这个数组卷积d-1次的结果,我现在就把具体过程解释一下),现在主要还是说明白这道题的推理。
那么具体如何卷呢?在一枚时候数组yige中,对应下标0到5,二枚的时候(点数2到12,假如数组是erge[])对应0-10,我们发现二枚的时候投出5点也就是求erge[4],需要的是erge[4]=yige[0]*yigr[4]+yige[1]*yige[3]+yige[2]*yige[2]+yige[3]*yige[1]+yige[4]*yige[0],
而我们再进一步考虑三枚色子的时候(数组为sange[]),例如要拿到5点 ,那么对应的种数sange[4]=erge[0]*yigr[4]+erge[1]*yige[3]+erge[2]yige[2]+erge[3]yige[1]+erge[4]yige[0];发现,对应的相乘的坐标之和跟所求的新目标的坐标是一样的也就是4=1+3=2+2=……,那么我们可以看到当我们要求的是d枚色子的时候,可以逐步从一枚色子开始计算对应的数组,最后得到d枚色子的数组。这里的一枚色子不再是6面了,而是f面,那么一枚的点数是1到f点,d枚的点数是d到df点了。对应的yige、erge、sange……的长度也是f、2f-1、3f-2……了
本方法的java代码示例:

/*卷积走起来,卷积的知识请学习《信号与系统》
 *作者@v7fgg
 *执行用时 :11 ms, 在所有 Java 提交中击败了84.68%的用户
 *内存消耗 :37 MB, 在所有 Java 提交中击败了100.00%的用户
 *2020年6月10日 19:47
 *效率还是可以的,实例 100 100 1000 用时496ms,答案126170734
 */
class Solution {
    public int numRollsToTarget(int d, int f, int target) {
        if(target<d||target>d*f){return 0;}
        //d个色子的号码和最小全1是d,最大全f就是d*f,在这范围之外的就不可能
        int ans[]=new int[d*f-d+1];//表示每种色子点数和的的组合数的数组
        System.arraycopy(juanji(d,f),0,ans,0,d*f-d+1);
        return ans[target-d];
    }
    public int[] juanji(int d,int f){
        //这个方法的作用是计算d个色子,每个色子f种号码时每种和的组合种数的
        int yige[]=new int[f];
        for(int i=0;i<f;i++){yige[i]=1;}
        //只有一枚色子的时候,每个点数自然就是1了
        if(d==1){return yige;}
        //从下面开始,就是d枚色子的数组应该是(d-1)枚色子的数组卷积1枚色子的数组(也就是卷积yige[])
        int ansL[]=new int[d*f-d-f+2];
        System.arraycopy(juanji(d-1,f),0,ansL,0,d*f-d-f+2);
        int ans[]=new int[d*f-d+1];
        for(int i=0;i<=d*f-d;i++){
            for(int j=0;j<f;j++){
                if(i-j>=0&&i-j<d*f-d-f+2){
                    ans[i]=(ans[i]+ansL[i-j])%1000000007;
                }                
            }
        }return ans;
    }
}
//每一轮求得juanji(d-1,f),记得arraycopy,不然多次直接写juanji(d-1,f),就多次调用
//实际上是很多次调用
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

可爱抱抱呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值