算法提高 概率計算(動態規劃)

試題 算法提高 概率計算

資源限制
時間限制: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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章