動態規劃解投硬幣的概率問題

逃課

時間限制:1000 ms  |  內存限制:65536 KB
描述

在一個星期三的早上,某同學想用扔硬幣的方式來決定是否要去上算法課。

他扔 n 次硬幣,如果當中有連續 m 次以上(含 m 次)的結果都是正面,那麼他就去上課,否則就接着睡覺。(假設每次扔硬幣扔出的正反兩面的概率都是 0.5。)

輸入

輸入的每行有一組數據,分別爲 n 和 m (0 < n <= 2000, 0 < m <= 10)。輸入以 0 0 結尾。

輸出

對於每組數據,輸出他去上課的概率,四捨五入保留小數點後 2 位。

樣例輸入
1 1
2 1
5 5
100 5
2000 5
2 3
0 0
樣例輸出
0.50
0.75
0.03
0.81
1.00
0.00

      

     動態規劃(dynamic programming),又稱dp問題,是求決策過程最優化的數學方法。即把多階段過程轉化爲一系列但階段問題,利用各階段之間的聯繫,逐個求解。至於具體的定義百度google一下,這裏就不再囉嗦了。簡單說一下我自己理解的動態規劃,類似於分治法(當然他們肯定不一樣),將期待解決的問題分解成若干個子問題,先求子問題,將每個子問題的解存入表中(因爲子問題之間不像分治法,分治法子問題之間是沒有聯繫的,而dp問題是有聯繫的,他的子問題需要用到其他子問題的解),然後最後求得待解決的問題。

     


       dp問題首先是需要打表,我們根據他的數據要求開一個數組,大小爲2001, dp[2001]。這個數組用來存放子問題的解,比如dp[i]就代表投完第i個硬幣時,已有連續m個正面的概率。具體假如m是2,dp[0],dp[1]肯定0,因爲投幣0次或1次不可能出現連續兩次正面。

   

分三種情況討論:


     1. n小於m,即dp數組下標i要小於m的時候,概率值都爲0,原因如上所述。


     2. 下標i等於m,也就是說n等m,投多少次剛好多少次是正面,這個概率是0.5的m次方


     3. 下標i大於m,即n大於m的時候,這個又要分兩種情況,概率是這兩種情況之和:


       (1). 在i之前已經有m次連續了,如果恰好前面第i-1個是連續m次,那第i次如果是正面的話就是連續m+1次符   合題目要求,如果第i次是反面的話那恰好就是m次也滿足題目,也就是說他第i次無論是正面還是反面都滿足條     件,所以dp[i]的概率第一種情況由dp[i-1]組成。


       (2). 第i次的時候恰好連續了m次,也就是說他前面m-1次都是正面,那他前面m次的那次是必須是一個反面,   否則不能剛好在第i次的時候連續m次正面了。他前面m次的那次是一個反面,那個反面的前一次有連續m次的概率是   dp[i-(m+1)],那他沒有連續m次正面的概率是1-dp[i-(m+1)],他後面的那次是反面,所以概率是0.5然後接着有m   次的0.5概率,即總共是0.5的m+1次方。第二種情況的概率是:dp[i-(m+1)]*pow(0.5, m),所以第三種情況的概率   是他們兩個之和:dp[i-1]+(1-dp[i-(m+1)])*pow(0.5, m)



http://www.bianchengla.com/course/ds/practise/problem?id=1321

C版(注意輸出)

  1. #include <stdio.h>  
  2. #include <math.h>  
  3. int main()  
  4. {  
  5.     double dp[2001];  
  6.     int m, n, i;  
  7.     scanf("%d", &n);  
  8.     scanf("%d", &m);  
  9.     while(n != 0 && m != 0)  
  10.     {  
  11.         for(i = 0; i < m; i++)  
  12.             dp[i] = 0;  
  13.         dp[m] = pow(0.5, m);  
  14.         for(i = m+1; i <= n; i++)  
  15.             dp[i] = dp[i-1] + (1-dp[i-m-1])*pow(0.5, m+1);  
  16.         printf("%.2lf/n", dp[n]);  
  17.         scanf("%d", &n);  
  18.         scanf("%d", &m);  
  19.     }  
  20.     return 0;  
  21. }  


C++版(注意輸出)

  1. #include <iostream>  
  2. #include <iomanip>  
  3. #include <cmath>  
  4. using namespace std;  
  5.   
  6. int main()  
  7. {  
  8.  //用動態規劃解這道題   
  9.  double dp[2001];  
  10.  //dp[i]代表投完第i個硬幣時,已有連續m個正面的概率。   
  11.  int n,m;  
  12.  cin>>n>>m;  
  13.  while(n!=0 && m!=0)  
  14.  {  
  15.   for(int i=0;i<m;i++)  
  16.    dp[i]=0;//當投幣次數小於m時,自然不可能有m個正面。   
  17.   dp[m]=pow(0.5,m);//投幣次數爲m,全是正面。   
  18.   for(int i=m+1;i<=n;i++)  
  19.    dp[i]=dp[i-1]+(1-dp[i-m-1])*pow(0.5,m+1);  
  20.    //投完第i個硬幣時,已有連續m個正面的情況分爲兩種:   
  21.    //1.投第i個之前已經有連續m個正面,概率爲dp[i-1]。  
  22.    //2.投完第i個時恰好有連續m個正面,這連續m個正面的前面一次是反面,  
  23.    // 這m+1次確定情況的概率是1/2的m+1次方;  
  24.    // 這m+1次以前沒有連續m個正面的概率爲1-dp[i-(m+1)]。   
  25.    // 所以投完第i個時恰好有連續m個正面的概率爲(1-dp[i-m-1])*pow(0.5,m+1)。   
  26.   cout<<fixed<<setprecision(2)<<dp[n]<<endl;  
  27.   cin>>n>>m;  
  28.  }  
  29.  return 0;  
  30. }  

     博文原址:http://blog.csdn.net/kingxueyuf/article/details/7238396


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章