题目大意
先手有n张牌,后手有m张牌,桌面上还有一张牌。n+m+1张牌互不相同且双方都知晓自己的牌是什么。
两人轮流操作,操作类型有二:
1、说桌面上的牌是什么,说对胜利,说错失败,执行此操作后游戏结束。
2、说一张牌,如果对方手里有则展示并弃置。
求先后手获胜概率。
神奇的概率题
我们来考虑一下第二个操作,我们可以选择
1、老老实实去试探对方的牌,称之为我是正昊
2、耍小聪明欺骗对方,报一张自己的牌,称之为我是水栓
这是我们的决策,对方也有决策,即认为我是正昊还是水栓。
设f(n,m)表示先手有n张牌后手有m张牌先手获胜的概率。
可以列个表
假设我们有p的概率成为正昊,1-p的概率成为水栓,那么对方因为要降低我们的收益,会在两种决策中选择较小的,因此获胜概率为min(p*m/(m+1)[1-f(m,n-1)]+1-p,p*m/(m+1)[1-f(m,n-1)]+p/(m+1)+1-p-(1-p)*f(m,n-1))
p是自变量,随手玩一玩发现他们是两条直线,一条斜率为正一条斜率为负。
我们的极值肯定取在他们的交点,对于f我们递归计算,然后接着解出p,就可以算出f(n,m)。
注意n*m=0就到边界了。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef double db;
int i,j,k,l,t,n,m;
bool bz[1200][1200];
db f[1200][1200];
void dfs(int n,int m){
if (bz[n][m]) return;
bz[n][m]=1;
if (n*m==0) f[n][m]=(db)1/(m+1);
else{
dfs(m-1,n);
dfs(m,n-1);
db a=f[m-1][n],b=f[m][n-1];
db p=(db)b*(m+1)/(b*(m+1)+1);
f[n][m]=(db)p*m/(m+1)*(1-a)+1-p;
}
}
int main(){
scanf("%d%d",&n,&m);
dfs(n,m);
printf("%.6lf %.6lf\n",f[n][m],1-f[n][m]);
}