单点时限: 2.0 sec
内存限制: 256 MB
/
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;
}