3446. 骰子点数之和问题

单点时限: 2.0 sec

内存限制: 256 MB

//********** Specification of Combination **********
double probability(int n, int k);
/* Precondition: n和k为整数
Postcondition: 返回n个骰子同时扔后点数之和为k的概率
*/
double probability(int n, int k) { //TODO: your function definition

}

#include <stdio.h>

int main() {
int n, k;
scanf("%d%d", &n, &k);
printf("%.6f\n", probability(n, k));
return 0;
}
骰子的六面分别是 1,2,3,4,5,6。

样例
input
2 6
output
0.138889
提示
每题共有 10 组测试数据。

对于测试点 1,2,3,1≤n≤3,输出与答案误差不超过 10−3 认为正确。
对于测试点 4,5,6,7,1≤n≤10,输出与答案误差不超过 10−3 认为正确。
对于测试点 8,9,10,1≤n≤2 000,输出与答案误差不超过 10−6 认为正确。
对于所有测试点,1≤k≤109。
思路一:题目:把n个骰子扔在地上,所有骰子朝上一面的点数之和为S。输入n,打印出S的所有可能的值出现的概率。
解题思路:
第一步,确定问题解的表达式。可将f(n, s) 表示n个骰子点数的和为s的排列情况总数。
第二步,确定状态转移方程。n个骰子点数和为s的种类数只与n-1个骰子的和有关。因为一个骰子有六个点数,那么第n个骰子可能出现1到6的点数。所以第n个骰子点数为1的话,f(n,s)=f(n-1,s-1),当第n个骰子点数为2的话,f(n,s)=f(n-1,s-2),…,依次类推。在n-1个骰子的基础上,再增加一个骰子出现点数和为s的结果只有这6种情况!那么有:

f(n,s)=f(n-1,s-1)+f(n-1,s-2)+f(n-1,s-3)+f(n-1,s-4)+f(n-1,s-5)+f(n-1,s-6) ,0< n<=6n
f(n,s)=0, s< n or s>6n

上面就是状态转移方程,已知初始阶段的解为:
当n=1时, f(1,1)=f(1,2)=f(1,3)=f(1,4)=f(1,5)=f(1,6)=1。
很显然思路一不行,数据太大了。
思路二:
用dp[i][j]表示第i次和位j的概率,初始时dp[1][1]=dp[1][2]=dp[1][3]=dp[1][4]=dp[1][5]=dp[1][6]=1/6。其实就是思路一的拓展。

//思路一递归代码:
int fun(int n, int count) {
    if (count < n || n< 1 || count > 6*n)
        return 0;
    if (n == 1)
        return 1;
    int ans = fun(n - 1, count - 1) + fun(n - 1, count - 2) + fun(n - 1, count - 3)
        + fun(n - 1, count - 4) + fun(n - 1, count - 5) + fun(n - 1, count - 6);
    return ans;
}`
`
//思路二:滚动数组+dp
#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n, k, x, y;
    cin>>n>>k;
    long double dp[7][12001];
    long double p=1.0/6.0;
    if(k>n*6 || k<n) {cout<<0; return 0;}
    for(int i=1; i<7; i++) dp[1][i]=p;
    for(int i=2; i<=n; i++)
    {
        x=i%7;//当前次
        y=(i-1)%7;//上一次
        for(int j=i; j<=6*i; j++)
        {
            dp[x][j]=0;
            if(j>6 && j>=i+5) {dp[x][j]+=dp[y][j-6]*p;}
            if(j>5 && j>=i+4) {dp[x][j]+=dp[y][j-5]*p;}
            if(j>4 && j>=i+3) {dp[x][j]+=dp[y][j-4]*p;}
            if(j>3 && j>=i+2) {dp[x][j]+=dp[y][j-3]*p;}
            if(j>2 && j>=i+1) {dp[x][j]+=dp[y][j-2]*p;}
            dp[x][j]+=dp[y][j-1]*p;
        }
    }
    printf("%.6llf\n",dp[n%7][k]);
    return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值