CF98EHelp Shrek and Donkey
Posted yoyoball
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF98EHelp Shrek and Donkey相关的知识,希望对你有一定的参考价值。
Portal --> CF98E
Description
两个人快乐玩牌(总共(n+m+1)张),(A)有(n)张牌,(B)有(m)张牌,还有一张反着放在桌面上,每个人都知道这(n+m+1)张牌有哪些,但是并不知道具体是那张,(A)只知道自己手头上的牌具体是哪些,(B)也是,这个时候两个人可以轮流进行操作,每次操作有两种选择:
(1)问对方手头上有没有某张牌,如果有那么对方要将这张牌丢掉,如果没有就说没有
(2)猜桌面上反着的那张牌是啥,猜错凉掉,猜对就win了,不管结果如何都是游戏结束
问先手和后手的胜率分别是多少(都采取最优决策)
Solution
这题考的是脑子qwq像我这种人要是去玩这个游戏估计输都不知道怎么输的。。
? 不过感觉整个思考的过程非常有意思qwq好好玩的题qwq
首先比较显然的一个事情就是猜牌这种那么高风险的事情当然是放到最后确定了才去猜,假设当前进行操作的一方为(A),另一方为(B),我们现在只考虑询问,那么有一个很重要的策略就是:(A)可以问(B)有没有自己手头上的牌
这个“阴险”的操作的目的是让(B)误以为(A)并没有这张牌,而同时(B)也知道自己没有这张牌,那如果是这样,(B)就会认为桌面上的牌是这张,一旦自认为确定了桌面上的牌是什么,(B)就会去猜,然后(B)会猜错那么(A)就win了
那么现在可以根据这个将(A)的询问分为两种:真询问(问的是(A)没有的牌),假询问(问的是(A)有的牌),接下来分两类讨论:
为了方面下面的描述,我们记(f(n,m))表示先手有(n)张牌后手有(m)张牌的时候先手的胜率
(1)如果说(A)进行的是一次真询问:
? 首先我们要根据(A)问的牌进行分类:
? 1、(A)问到的是桌上那张(有(frac{1}{m+1})的概率)
? 这个时候如果说(B)认为(A)在做真询问那么(B)就直接猜中桌上那张是什么了,(A)的胜率为(0)
如果说(B)认为(A)在做假询问那么(B)接下来永远不会猜这张牌,那必定凉掉,此时(A)胜率为 (1)
2、(A)问到的是(B)手头上的一张(有(frac{m}{m+1})的概率)
? (B)会丢掉这张牌,然后变成(B)先手,游戏继续,此时(A)的胜率是(1-f(m-1,n))
(2)如果说(A)进行的是一次假询问:
这里就可以直接按照(B)的判断分类讨论了:
? 1、如果说(B)认为(A)在做真询问,那么(B)就会认为桌上那张就是这张,然后凉掉,(A)的胜率为(1)
2、如果说(B)认为(A)在做假询问,那么就相当于(B)多确定了一张牌,也就是说(A)这张牌相当于“废了”,我们可以直接视为(A)将这张牌丢掉然后(B)变成先手继续游戏,也就是说这个时候(A)的胜率是((1-f(m,n-1)))
? 现在不确定的东西就是,(A)做真询问还是假询问
? 我们假设(B)每次有(p)的概率认为(A)在做真询问,因为(B)十分厉害(采取最优策略什么的)所以应该是在“认为是真询问”和“认为是假询问”里面取min,那么我们可以列出dp式子:
[
egin{aligned}
B认为真&:f(n,m)=p(frac{m}{m+1}(1-f(m-1,n))+(1-p)B认为假&:f(n,m)=p(frac{m}{m+1}(1-f(m-1,n)+frac{1}{m+1}))+(1-p)(1-f(m,n-1))\end{aligned}
]
然后(f(n,m))的实际值应该就是这两个里面取最小了
那么现在(A)想让这个(f(n,m))尽量大,而(A)能做的就是决定这个(p),所以我们现在要解决的问题其实相当于求一个(p)使得上面两个式子的最小值最大,注意到如果将(p)看成未知数的话,上面两个式子可以看成(p)的一个一次函数,并且一个递增一个递减,那所以直接取交点就好了
? 实现上的话。。为了方便选择递归转移
? 代码大概长这个样子
? (写的时候放弃思考qwq其实完全可以先手动化简一波这样式子会简洁很多qwq)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1010;
double f[N][N];
int n,m;
void solve(int n,int m){
if (f[n][m]!=-1) return;
if (n==0) f[n][m]=1.0/(1.0*m+1.0);
else if (m==0) f[n][m]=1;
if (f[n][m]!=-1) return;
solve(m-1,n);
solve(m,n-1);
double k1=f[m][n-1]-(1.0*m)/(1.0*m+1.0)*f[m-1][n],b1=1.0-f[m][n-1];
double k2=(1.0*m)/(1.0*m+1.0)*(1.0-f[m-1][n])-1.0,b2=1.0;
double p=(b2-b1)/(k1-k2);
f[n][m]=k1*p+b1;
return;
}
int main(){
#ifndef ONLINE_JUDGE
//freopen("a.in","r",stdin);
#endif
scanf("%d%d",&n,&m);
int mx=max(n,m);
for (int i=0;i<=mx;++i)
for (int j=0;j<=mx;++j)
f[i][j]=-1;
solve(n,m);
printf("%.9lf %.9lf
",f[n][m],1.0-f[n][m]);
}
以上是关于CF98EHelp Shrek and Donkey的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces 98E Help Shrek and Donkey 游戏策略神题
(CF#257)B. Jzzhu and Sequences