#数据结构与算法学习笔记#剑指Offer65:n个骰子的点数和 + 详细解析(Java、C/C++)

2019.3.6     《剑指Offer》从零单刷个人笔记整理(66题全)目录传送门​​​​​​​

这道书上的题牛客网没有,不知道是不是漏掉了。

求n个骰子所有可能的情况其实是固定的,一共6^{n}次。因此求所有点数和s的概率实际上要的是求所有点数和s出现的次数。

由于每一粒骰子的情况都是固定的,因此多粒骰子的情况实际上就是每一种情况的不断累加的结果。

只有1粒骰子的时候,结果是分布是:1,2,3,4,5,6

当有2粒骰子的时候,把1-6分别加至1粒骰子的结果分布:

1 + 1,2,3,4,5,6  —— 2,3,4,5,6,7

2 + 1,2,3,4,5,6  —— 3,4,5,6,7,8

3 + 1,2,3,4,5,6  —— 4,5,6,7,8,9

4 + 1,2,3,4,5,6  —— 5,6,7,8,9,10

5 + 1,2,3,4,5,6  —— 6,7,8,9,10,11

6 + 1,2,3,4,5,6  —— 7,8,9,10,11,12

结果的分布是:2,3,4,5,6,7,8,9,10,11,12

很容易能够想到,当有n粒骰子时候,要把1-6分别加至n-1粒骰子的结果分布上。很明显的一种递归思想。

递归法:

1. 创建一个6n - (n - 1)的结果数组result(6n为最大可能的结果,n - 1为排除掉的最小数字。result的可能确定,当分布不定)

2. 将n粒骰子分为a堆(1粒)和b堆(n-1粒),将a堆的骰子点数1-6加至b堆的所有点数和sum之中,骰子总数idx-1。

3. 当骰子加至最后一个,即idx为1时,骰子出现一种可能的情况,这种情况的结果为sum,对结果数组result[sum - number]中的值+1。

4. 递归进行1-3过程,程序运行会出现n层递归一种6^{n}种可能的递归分支,其分布最终记录result数组中。

 

递归法存在n总数过大导致内存崩溃的问题,还可以逆向思考,转换一种思路。

将关注点着眼在结果数组result上,观察result数值的变化。

只有1粒骰子的时候,

结果分布:1,2,3,4,5,6

出现次数:1,1,1,1,1,1

当有2粒骰子的时候,

结果分布:2,3,4,5,6,7,8,9,10,11,12

出现次数:1,2,3,4,5,6,5,4, 3 , 2 , 1 

result分布上的变化实际上是由新加入的2号骰子对1号骰子结果分布的不断更新。

1  —— 0

2  —— 1 + 1

3  —— 1 + 2,2 + 1

4  —— 1 + 3,2 + 2,3 + 1

5  —— 1 + 4,2 + 3,3 + 2,4 + 1

6  —— 1 + 5,2 + 4,3 + 3,4 + 2

7  —— 1 + 6,2 + 5,3 + 4,4 + 3,5 + 2,6 + 1 

8  —— 2 + 6,3 + 5,4 + 4,5 + 3,6 + 2

9  —— 3 + 6,4 + 5,5 + 4,6 + 3

10 —— 4 + 6,5 + 5,6 + 4

11 —— 5 + 6,6 + 5

12 —— 6 + 6

经过观察可以发现,第i-1号骰子的result_old(min—max)的分布,会对第i号骰子结果result_new的分布(min+1—max+6)产生贡献。result_new中结果num的出现次数等于num-6至num-1所有出现次数的和,即

result_new[num] = result_old[num - 1] + result_old[num - 2] + result_old[num - 3] + result_old[num - 4] + result_old[num - 5] + result_old[num - 6]

因为前i-1个骰子所得的结果num-x(x=1-6)的结果加上第i号骰子得出的x,即可产生新的结果num。以此类推。

循环法:

1. 创建一个6n的结果数组result(6n为最大可能的结果)

2. 记录第1粒骰子的初始result值,result[j](j = 1-6)均为1

3. 从第2粒骰子开始,对result进行更新。对于第i粒骰子,将result[j](j = i—6*i)中的结果进行更新,result[j]等于result[j-k](k=1-6)的和。

4. 将结果数组进行复制保存(或者将i之前的所有次数重置为0)

5. 循环进行步骤3-4

 


题目描述

把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s所有可能值出现的概率。


Java实现:

/**
 * 
 * @author ChopinXBP
 * 把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s所有可能值出现的概率
 * 
 */

public class PrintProbability_66 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int number = 6;
		int[] result = PrintProbability(6);
		int[] result2 = PrintProbability1(6);
		for (int i = 0; i <= number * 6 - number; i++) {
			System.out.println("i: " + i + " count: " + result[i]);
		}
		System.out.println("  ");
		for (int i = number; i <= number * 6; i++) {
			System.out.println("i: " + i + " count: " + result2[i]);
		}
		System.out.println("  ");

	}


	//方法一:递归法
	public static int[] PrintProbability(int number) {
		int[] result = new int[number * 6 - number + 1];		
		for (int i = 1; i <= 6; i++) {
			probability(number, number, i, result);
		}		
		return result;
	}
	//将骰子分为两堆,a堆有1个,b堆有idx-1个
	public static void probability(int number, int idx, int sum, int[] result) {
		//当骰子只剩一个时,将其点数和进行记录
		if (idx == 1) {
			result[sum - number]++;
		}
		else {
			//将a堆的骰子点数1-6加至b堆的所有点数和。
			for (int i = 1; i <= 6; i++) {
				probability(number, idx - 1, sum + i, result);
			}
		}
	}
	
	
	//方法二:循环法
	public static int[] PrintProbability1(int number) {

		int[] result = new int[6 * number];
		
		// 第一次抛掷骰子
		for (int i = 1; i <= 6; i++) {
			result[i] = 1;
		}
		
		for(int i = 2; i <= number; i++) {
			int[] temp = new int[6 * number + 1];
			// i骰子的出现次数j等于之前次数为j-k(k=1-6)的次数总和。k不能大于j
			for(int j = i; j <= 6 * i; j++) {
				for(int k = 1; k <= j && k <= 6; k++) {
					temp[j] += result[j - k];
				}
			}
			result = temp;
		}

		return result;
	}
}

C++实现示例:

#include "stdafx.h"
#include 

int g_maxValue = 6;
 
// ====================递归法====================
void Probability(int number, int* pProbabilities);
void Probability(int original, int current, int sum, int* pProbabilities);

void PrintProbability_Solution1(int number)
{
    if(number < 1)
        return;
 
    int maxSum = number * g_maxValue;
    int* pProbabilities = new int[maxSum - number + 1];
    for(int i = number; i <= maxSum; ++i)
        pProbabilities[i - number] = 0;
 
    Probability(number, pProbabilities);
 
    int total = pow((double)g_maxValue, number);
    for(int i = number; i <= maxSum; ++i)
    {
        double ratio = (double)pProbabilities[i - number] / total;
        printf("%d: %e\n", i, ratio);
    }
 
    delete[] pProbabilities;
}
 
void Probability(int number, int* pProbabilities)
{
    for(int i = 1; i <= g_maxValue; ++i)
        Probability(number, number, i, pProbabilities);
}
 
void Probability(int original, int current, int sum, 
                 int* pProbabilities)
{
    if(current == 1)
    {
        pProbabilities[sum - original]++;
    }
    else
    {
        for(int i = 1; i <= g_maxValue; ++i)
        {
            Probability(original, current - 1, i + sum, pProbabilities);
        }
    }
} 

// ====================循环法====================
void PrintProbability_Solution2(int number)
{
    if(number < 1)
        return;

    int* pProbabilities[2];
    pProbabilities[0] = new int[g_maxValue * number + 1];
    pProbabilities[1] = new int[g_maxValue * number + 1];
    for(int i = 0; i < g_maxValue * number + 1; ++i)
    {
        pProbabilities[0][i] = 0;
        pProbabilities[1][i] = 0;
    }
 
    int flag = 0;
    for (int i = 1; i <= g_maxValue; ++i) 
        pProbabilities[flag][i] = 1; 
    
    for (int k = 2; k <= number; ++k) 
    {
        for(int i = 0; i < k; ++i)
            pProbabilities[1 - flag][i] = 0;

        for (int i = k; i <= g_maxValue * k; ++i) 
        {
            pProbabilities[1 - flag][i] = 0;
            for(int j = 1; j <= i && j <= g_maxValue; ++j) 
                pProbabilities[1 - flag][i] += pProbabilities[flag][i - j];
        }
 
        flag = 1 - flag;
    }
 
    double total = pow((double)g_maxValue, number);
    for(int i = number; i <= g_maxValue * number; ++i)
    {
        double ratio = (double)pProbabilities[flag][i] / total;
        printf("%d: %e\n", i, ratio);
    }
 
    delete[] pProbabilities[0];
    delete[] pProbabilities[1];
}

#Coding一小时,Copying一秒钟。留个言点个赞呗,谢谢你#

你可能感兴趣的:(C/C++,数据结构与算法,JAVA,剑指Offer)