面试题 43,n个骰子的点数

这道题要用递推的思想做。

假设骰子是最常见的骰子,有6个面,1-6

对于n个骰子,其和的可能性有n到6n这么多种情况。

(1) 假设f(m) 表示骰子数目为n时,和为m的可能性的数量,当骰子数量从n变为n+1,由于组合更多,之前计算好的和为m的可能性的数量也会变化。

考虑到新的骰子可以从1变到6,要想让所有骰子和依然为m,那么其余的骰子的和就需要从m-6变到m-1,也就是说对于 m <= 6n,得出公式1: f(m) = f(m-6) + f(m-5) + ... + f(m-1) 

(2) 第n+1颗骰子的加入会让和的上限从原来的 6n变为 6(n+1),那么这多出来的6个值:f(6n+1)到f(6n+6) 怎么计算?

以f(6n+1)为例,我们依然考虑新骰子可以从1变到6,其余的n个骰子的和就需要从6n变到6n-5,因此f(6n+1)依然可以用上面的公式做。

对于f(6n+2)呢?新骰子可以从1变到6,其余的n个骰子的和就需要从6n+1变到6n-4,而n个骰子是不可能和为6n+1的,所以f(6n+2) = f(6n) + ... + f(6n-4),和上面的公式1相比,少了f(m-1),但是如果我们用数组表示的话,我们可以假设所有初值为0,这样当我们计算出f(6n)以及之前的所有值的时候,f(6n+1)仍然为0,因此 f(6n+2) = f(6n+1) + f(6n) +... + f(6n-4)依然成立。

 

所以,f(m) = f(m-6) + f(m-5) + ... + f(m-1)。

我们用数组来实现,需要注意的是,我们必须用两组数组来做,一个数组表示n个骰子情况下所有和的情况,然后计算出n+1情况下各种和的值存到另一个数组中。两个数组相互用。

可以用m&1表示当前要计算的数组,1-m&1表示另一个数组。

 

我自己的代码,没有经过严格检验。

#include <stdio.h>

double* CalcuSumRatio(int num, const int MaxValue){
    if(num <= 0) return NULL;
    int** sum = new int*[2];
    int i = 0, j = 0, k = 0;
    for(i = 0; i < 2; i++){
        sum[i] = new int[MaxValue * num + 1];
        for(j = 0;j < MaxValue * num + 1; j++)
            sum[i][j] = 0;
    }
    
    for(k = 1; k <= MaxValue; k++)
        sum[1][k] = 1;
    for(i = 2; i <= num; i++){
        //printf("Round %d:---------------\n", i);
        for(j = 0; j < i; j++)
            sum[i & 1][j] = 0;
        for(j = i; j <= MaxValue * i; j++){
            sum[i & 1][j] = 0;
            for(k = 1; k <= MaxValue; k++){
                if((j-k) > 0){
                    sum[i & 1][j] += sum[1 - i&1][j - k];
                }
            }
            //printf("%d: %d\n", j, sum[i & 1][j]);
        }
    }
    double* Radio = new double[MaxValue * num + 1];
    long long totalSumNum = 0;
    for(i = num; i < MaxValue * num + 1; i++)
        totalSumNum += sum[num&1][i];
    for(i = 0; i < num; i++)
        Radio[i] = 0;
    for(i = num; i < MaxValue * num + 1; i++){
        Radio[i] = (sum[num&1][i] * 1.0)/totalSumNum;
        printf("%d: %e\n", i, Radio[i]);
    }
        
    return Radio;
}


int g_maxValue = 6;

// ====================测试代码====================
void Test(int n)
{
    printf("Test for %d begins:\n", n);

    double* ratio = CalcuSumRatio(n, g_maxValue);

    printf("\n");
}

int main()
{
    Test(1);
    Test(2);
    Test(3);
    Test(4);
    
    Test(11);

    Test(0);

    return 0;
}

 

其实这道题用递归做更加直观,但是效率比上面的循环实现就要低了。

书上代码:

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];
}

 

posted on 2014-03-09 01:28  Felix Fang  阅读(313)  评论(0)    收藏  举报

导航