描述
扔 n 个骰子,向上面的数字之和为 S。给定 Given n,请列出所有可能的 S 值及其相应的概率。
样例
给定 n = 1
,返回 [ [1, 0.17], [2, 0.17], [3, 0.17], [4, 0.17], [5, 0.17], [6, 0.17]]
。
这是剑指offer的一道题,主要思想为动态规划,由于投n个骰子出现的点数为n~6n,一共为6n-n+1=5n+1个数,所以我们需要申请一个长为5n+1的辅助数组a。数字的下标[i]表示出现的和(0表示n个骰子出现的最小的和,即n),下标对应的值a[i]表示对应的和出现的次数。
从最简单的开始,假设只有一个骰子,需要长度为6的辅助数组,每个骰子出现的次数为1,于是最后的数组a的长度为6,对应的值为[1,1,1,1,1,1]。假如有两个骰子,我们把骰子的和分为前n-1个骰子的和和第n个骰子的值的相加和,前n-1即2-1=1,即前1个的骰子的和的数组为[1,1,1,1,1,1],由于两个骰子的和的范围为2~12,以和为10为例,10可以分为前n-1组的值+第n个骰子的值,则:
10=1+9=2+8=3+7=......=9+1
由于第二个骰子出现的值为1~6,所以10只可能由以下的值组成4+6=5+5=6+4=7+3=8+2=9+1,所以和为10出现的次数等于和为4,5,6,7,8,9的次数相加而得,即f(n)=f(n-1)+f(n-2)+f(n-3)+f(n-4)+f(n-5)+f(n-6),所以这就是递归规律,这里需要注意,我们需要一个新的临时数组tmp来保存生成的值,最后再把tmp的值重新赋值给a。所以程序的思想如下,如果n=1,则通过递推公式构造数组a的0~5(下标)的数,如果n=2,则先构造0~5,再通过0~5构造0~10的数(因为2个骰子出现的和为2~12,归一到下标为0即0~10),往后以此类推。最后需要注意的一点是,由于在递归过程中数组的值(出现的次数)是指数次的增加,所以辅助数组需定义为long long,否则如果定义为int则会溢出(int最大为2147483647),以下为代码(c++,在lintcode上已ac)
//最后的答案返回格式为vector<pair<int, double>> vector<pair<int, double>> dicesSum(int n) { vector<pair<int, double>> result; //辅助数组a long long *array = new long long[5 * n + 1]; memset(array, 0, sizeof(long long)*(5 * n + 1)); dicesSumCore(n, array, result); delete[] array; return result; } //构造递推数组 void calculate(long long* array, int i, int n) { //临时标量tmp,作用上文已说过 long long tmp[10001]; memset(tmp, 0, sizeof(long long)*(10001)); //sum表示第i项~第(i-6)项的和 long long sum = 0; for (int j = 0; j <= 5 * (i + 1); j++) { //前6项一直累加 if (j <= 5) { sum += (array)[j]; tmp[j] = sum; } else { //后面的需要减去第i-6项的值 sum += (array)[j] - (array)[j - 6]; tmp[j] = sum; } } //最后把tmp的值赋给a for (int j = 0; j <= 5 * (i + 1); j++) { array[j] = tmp[j]; } } void dicesSumCore(int n, long long* &array, vector<pair<int, double>> & result) { //初始化前6个值为1 for (int i = 0; i < 6; i++) { array[i] = 1; } //如果n==1,则初始化后直接计算并赋值到result中返回 if (n == 1) { for (int i = 0; i < 6; i++) { pair<int, double> zy; zy.first = i + 1; zy.second = array[i] / pow(6, n); result.push_back(zy); } return; } //i表示第几次递推,每次递推都构造一个n-6n的数组,注意i的下标从0开始,所以i=1表示n=2的第二次构造,构造的数组下标为0-5n(包含5n) for (int i = 1; i < n; i++) { //构造第i+1次递推数组 calculate(array, i, n); //最后一次经过calculate构造后赋值到result中 if (i == n - 1) { for (int i = 0; i <= 5 * n; i++) { pair<int, double> zy; zy.first = i + n; zy.second = array[i] / pow(6, n); result.push_back(zy); } } } }