n个骰子求和

 

描述

扔 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);
			}
		}
	}

}

递归解法:

/****************************
func:获取n个骰子指定点数和出现的次数
para:n:骰子个数;sum:指定的点数和
return:点数和为sum的排列数
****************************/
class Solution {
public:
    int dicesSumCore(int n,int sum){
        if(n<1 || sum<n || sum>6*n) return 0;
        if(n==1) return 1;
        return (dicesSumCore(n-1,sum-6)+dicesSumCore(n-1,sum-5)
        +dicesSumCore(n-1,sum-4)+dicesSumCore(n-1,sum-3)
        +dicesSumCore(n-1,sum-2)+dicesSumCore(n-1,sum-1));
    }
    /**
     * @param n an integer
     * @return a list of pair<sum, probability>
     */
    vector<pair<int, double>> dicesSum(int n) {
        // Write your code here
        vector<pair<int, double>> res;
        double sum_count=pow(6,n);
        for(int i=n;i<=6*n;i++){
            res.push_back({i,(dicesSumCore(n,i)/sum_count)});
        }
        return res;
    }
};

非递归解法(注意归零操作和long long int操作):

class Solution {
public:
    void dicesSumCore(int n,vector<long long int> &sum){
        sum[0]=0;
        sum[1]=sum[2]=sum[3]=sum[4]=sum[5]=sum[6]=1;
        if(n==1) return;
        for(int i=2;i<=n;i++){
            //把相隔两个单位以上的清零,比如当i=3时,只考虑i=2时产生的2~12,所以必须把sum[0]=sum[1]=0清零
            for (int t = 0; t < i-1; t++) sum[t] = 0; 
            for(int j=6*i;j>=i;j--){
                long long int tmp1=((j-1)>=0?sum[j-1]:0);
                long long int tmp2=((j-2)>=0?sum[j-2]:0);
                long long int tmp3=((j-3)>=0?sum[j-3]:0);
                long long int tmp4=((j-4)>=0?sum[j-4]:0);
                long long int tmp5=((j-5)>=0?sum[j-5]:0);
                long long int tmp6=((j-6)>=0?sum[j-6]:0);
                sum[j]=tmp1+tmp2+tmp3+tmp4+tmp5+tmp6;
            }
        }
    }
    /**
     * @param n an integer
     * @return a list of pair<sum, probability>
     */
    vector<pair<int, double>> dicesSum(int n) {
        // Write your code here
        vector<pair<int, double>> res;
        vector<long long int> sum(6*n+1,0);
        dicesSumCore(n,sum);
        double sum_count=pow(6,n);
        for(int i=n;i<=6*n;i++){
            res.push_back({i,(sum[i]/sum_count)});
        }
        return res;
    }
};

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值