【BZOJ 1419】Red is good [概率DP]

我 是 Z Z

概率好玄啊(好吧是我太弱.jpg

Description

桌面上有R张红牌和B张黑牌,随机打乱顺序后放在桌面上,开始一张一张地翻牌,翻到红牌得到1美元,黑牌则付出1美元。可以随时停止翻牌,在最优策略下平均能得到多少钱。

数据范围与提示

输出答案时,小数点后第六位后的全部去掉,不要四舍五入.

Solution

乍一看感觉是道普通求期望,然后就在错误的路上速度与激情。。其实不难,但也是道挺好的概率dp

由于要求的是最佳方案,所以并不是所有方案的平均值,即不能把期望作为最终答案。所以该怎么做?遇事不决就dp啊!

考虑开二维状态数组f[r][b],一维已抽出的红牌,一维已抽出的黑牌。那么对于f[i][j],有i/(i+j)的平均最优解是由抽出红牌转移而来,j/(i+j)的平均最优解是由抽出黑牌转移而来。即:

f[i][j]=(i×(f[i-1][j]+1)+j×f[i][j-1])/(i+j)

又因为要求最优解,所以抽牌赔钱不如不抽,即可将值为负的状态转移为0。

所以有状态转移方程:

f[i][j]=max(0.0,((f[i-1][j]+1)×i+(f[i][j-1]-1)×j)/(i+j))

几点注意:

1.输出不能四舍五入,我在网上搜的几种避免四舍五入的方式不知为何都没用,只能手模了一个。。

1 ans=((int)(ans*1e6))/1e6;
2 printf("%.6lf",ans);

 

2.题面空间限制64m,5000×5000显然会炸,要开滚动数组。


代码如下:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int r,b,n;
 4 double f[5001][5001],ans;
 5 inline int read()
 6 {
 7     int x=0,f=1;
 8     char ch=getchar();
 9     while(ch<'0'||ch>'9')
10     {
11         if(ch=='-')
12             f=-1;
13         ch=getchar();
14     }
15     while(ch>='0'&&ch<='9')
16     {
17         x=(x<<1)+(x<<3)+(ch^48);
18         ch=getchar();
19     }
20     return x*f;
21 }
22 int main()
23 {
24     r=read();   b=read();
25     for(int i=1;i<=r;i++)
26     {
27         f[i&1][0]=i;
28         for(int j=1;j<=b;j++)   
29             f[i&1][j]=max(0.0,((f[i-1&1][j]+1)*i+(f[i&1][j-1]-1)*j)/(i+j));
30     }
31     ans=((int)(f[r&1][b]*1e6))/1e6;
32     printf("%.6lf",ans);
33     return 0;
34 }
蒟蒻代码QAQ

(附身边高人JYFHYX T60的暴力搜索:

 1 using namespace std;
 2 int j,d;
 3 double r,b;
 4 double f[5001][5001];
 5 inline double dp(double black,double red)
 6 {
 7 j=black,d=red;
 8 if(f[j][d])
 9 return f[j][d];
10 if(black+red==0)
11 return 0;
12 return f[j][d]=(red/(black+red))(max(dp(black,red-1.0),0.0)+1)+(black/(black+red))(max(dp(black-1.0,red),0.0)-1);
13 }
14 int main()
15 {
16 cin>>r>>b;
17 int n=r+b;
18 for(int i=1;i<=r;i++)
19 {
20 f[0][i]=i;
21 }
22 for(int i=1;i<=b;i++)
23 f[i][0]=-i;
24 double ans;
25 ans=dp(b,r);
26 ans=((int)(ans*1e6))/1e6;
27 printf("%.6lf",ans);
28 }
爆搜

 

#include<bits/stdc++.h>
using namespace std;
double r,b;
double f[5001][5001];
inline double dp(double black,double red)
{
    int j=black,b=red;
    if(f[j][b])
    return f[j][b];
    if(black+red==0)
    return 0;
    return f[j][b]=(red/(black+red))*(max(dp(black,red-1.0),0.0)+1)+(black/(black+red))*(max(dp(black-1.0,red),0.0)-1);
}
int main()
{
    cin>>r>>b;
    int n=r+b;
    for(int i=1;i<=r;i++)
    {
        f[0][i]=i;
    }
    for(int i=1;i<=b;i++)
    f[i][0]=-i;
    double ans;
    ans=dp(b,r);
    ans=((int)(ans*1e6))/1e6;
    printf("%.6lf",ans);
}
缩进版

 

posted @ 2021-05-22 18:51  keen_z  阅读(39)  评论(1编辑  收藏  举报