试题 算法提高 概率计算
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
生成n个∈[a,b]的随机整数,输出它们的和为x的概率。
输入格式
一行输入四个整数依次为n,a,b,x,用空格分隔。
输出格式
输出一行包含一个小数位和为x的概率,小数点后保留四位小数
样例输入
2 1 3 4
样例输出
0.3333
数据规模和约定
对于50%的数据,n≤5.
对于100%的数据,n≤100,b≤100.
题解思路:
题目就是从a到b中挑n个数,使得这n个数字的和为x。那么所求的概率就是所有挑的种数除以总的方案数,总的方案数显然为 (a-b+1)^ n 。注意:不能把可行的方案数求出来然后除以总的方案数,因为总的方案数可能高达 100^ 100 ,是个超级的大数了,当然可以通过大数的除法解决这一问题(请看我的另一篇博文 >< 大数四则运算),但是代码可能多一倍。推荐的做法是,在每次求得方案数就除以 a-b+1 ,因为要选n个数,所以总共除了 n次,正好满足要求。
本题有递推解法和递归解法,本文用的是递归解法,递推解法请看https://blog.csdn.net/go_accepted/article/details/57105651
当选第n次数的时候,要求挑的数的总和为X,如果第n-1 次数的总和(记为m)加上
[a,b]中的一个数正好为X,那么就可以在第n次凑满X。就这样一直往前推,如果推到第0次
的时候和为0,那么是符合条件的,其他情况:n为0但是left为整数(还没凑满X)或者n不
为0,但是left为0(已经凑满了X,但是还要挑数,显然也不会符合条件),这些情况要立
即剪枝,返回0.然后配合记忆化搜索,当dp[n][m] 已经计算过了,后边就可以直接用,因
为 a到b之间的数可以随意挑,不是总共只能挑一次。
状态: dp[n][m] 挑n次数,凑满m的概率 dp[n][X]就是答案
状态转移:
for (int i = A; i <= B; i++)
dp[num][left] += dfs(num - 1, left - i);
代码如下
#include<bits/stdc++.h>
using namespace std;
int N, A, B, X;
float dp[101][10001];
float dfs(int num, int left) { //还需要挑num个数,凑满left
if (num==0&&left == 0) //满足条件
return 1;
if (num <= 0 || left <= 0) //均不满足条件
return 0;
if (dp[num][left] != -1) //记忆化搜索
return dp[num][left]/(B-A+1);
dp[num][left] = 0;
for (int i = A; i <= B; i++)
dp[num][left] += dfs(num - 1, left - i);
return dp[num][left]/(B-A+1); //注意这里!!!
}
int main() {
scanf("%d%d%d%d", &N, &A, &B, &X);
for (int i = 0; i <= N; i++)
for (int j = 0; j <= X; j++)
dp[i][j] = -1;
float ans=dfs(N, X);
printf("%.4f", ans);
return 0;
}