动态规划解投硬币的概率问题

逃课

时间限制: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


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