[ACdream]哗啦啦村的日常游戏(一)抓个球[概率DP][记忆化搜索]

F - 哗啦啦村的日常游戏(一)抓个球

Time Limit:  2000/1000MS (Java/Others)     Memory Limit:  128000/64000KB (Java/Others)
Problem Description

唐老师和狗哥在玩哗啦啦村的日常游戏——抓个球。

在袋子里有w个白球,b个黑球。

唐老师和狗哥轮流从袋子里抓球,谁先抓到白球谁就胜利。

但是,令唐老师和狗哥没想到的是,小彭玉居然来捣乱了!

每当唐老师和狗哥一回合抓球结束后(各抓取一个球),小彭玉会偷偷的抓取一个球走。

那么这时候,问题来了,当唐老师先手的时候,唐老师获胜的概率是多少呢?

假设所有抓球都是随机的。

Input
只有两个数字 w和b w<=1000 b<=1000
Output
输出唐老师赢的概率 保留9位小数
Sample Input
1 3
Sample Output

0.500000000

解题思路:我们用e[w][b]记录白球为w个,黑球为b个时,唐老师胜利的概率。然后注意到子局面有两种情况:一、小彭玉拿了白球,此时子局面为e[w - 1][b - 2];二、小彭玉拿了黑球,此时子局面为e[w][b - 3]。然后子局面一直递归下去,最终到达e[0][0]或者e[1][0]或者e[0][1](这三个局面都可以直接判断结果,输出返回值)。从而得出解。

另外关于记忆化搜索和dp的区别可以参考这篇博文:http://m.blog.csdn.net/blog/dragonu011639256/29389951
具体代码如下:(记忆化搜索版本)

#include<iostream>
#include<cctype>
#include<cstdio>
#include<algorithm>
using namespace std;
double e[1011][1011];

double dfs(int w, int b)
{
    if(e[w][b] >= 0.0) return e[w][b];
    double ww = w, bb = b;
    double ret = ww / (ww + bb);
    //小彭玉拿了黑球。注意这里概率的计算,要将三者的概率一同相乘
    if( b >= 3)
        ret += bb / (ww + bb) * (bb - 1) / (ww + bb - 1) * (bb - 2) / (ww + bb - 2) * dfs(w, b - 3);
    //小彭玉拿了白球
    if( w >= 1 && b >= 2)
        ret += bb / (ww + bb) * (bb - 1) / (ww + bb - 1) * ww / (ww + bb - 2) * dfs(w - 1, b - 2);
    return e[w][b] = ret;
}

int main()
{
    int w, b; scanf("%d%d", &w, &b);
    for(int i = 0; i <= w; ++i)
        for(int j = 0; j <= b; ++j)
            e[i][j] = -1;
    e[0][0] = 0, e[0][1] = 0, e[1][0] = 1;
    printf("%.9f\n", dfs(w, b) );
    return 0;
}
DP版本:(感谢舍友倾情提供)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
double dp[1024][1024];
using namespace std;
double slove(int i,int j){
	double t1=j*1.0/((i+j)*1.0);
	double t2=dp[i-3][j]*(i-2)*(i-1)*1.0/((i+j-1)*(i+j-2)*1.0);
	double t3=dp[i-2][j-1]*(i-1)*(j)*1.0/((i+j-1)*(i+j-2)*1.0);
	//cout<<t1<<endl<<t2<<endl<<t3<<endl;
	return t1+(1-t1)*(t2+t3);
}
int main(){
    int b,w;
    cin>>w>>b;
    for(int i=1;i<=w;i++){
    	dp[0][i]=1.0;
    	dp[1][i]=1.0*i/(1.0+1.0*i);
    	dp[2][i]=1.0*i/(2.0+1.0*i)+2.0/((i*1.0+2.0)*(i*1.0+1.0));
    }
    for(int i =0;i<=b;i++) dp[i][0]=0.0;
    	dp[2][1]=1.0/3.0;
    for(int i=3;i<=b;i++){
    	for(int j=1;j<=w;j++){
    		dp[i][j]=slove(i,j);
    	}
   }
    printf("%.9f\n",dp[b][w]);
    return 0;
}




 
  

 
  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值