【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 }
(附身边高人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); }