算法提高 概率计算(动态规划)

试题 算法提高 概率计算

资源限制
时间限制: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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值