A A A 和 B B B 两个人比赛射飞镖,规则如下:
两个人轮流射飞镖,初始分数都为 N N N,假设射中区域 x x x,如果 x ≤ N x\le N x≤N,分数减去 x x x;否则分数保持不变。第一个把分数清零的玩家获胜。
A A A 每次随便射飞镖,所以他射到每个区域的概率都相等。
B B B 有策略地射飞镖,他能够保证落到三块连续的区域内,且三块之间的概率相等。
假设初始值都为 N N N,问 A A A 先手获胜的概率和 B B B 先手获胜的概率。
1 ≤ N ≤ 501 1\le N\le501 1≤N≤501
标靶权值为:20,1,18,4,13,6,10,15,2,17,3,19,7,16,8,11,14,9,12,5。(包含1~20所有的数字)
如果 N N N 小于等于20,对于 A A A 和 B B B 来说,每次射飞镖只有两种状态:射中的值刚好为当前分数 N N N,或者没射中。就算射中一个分数小于 N N N 的,对于这个状态来说没有改变。所以此时 B B B 的策略肯定要瞄准 N N N 去射。
d p 1 [ i ] [ j ] dp1[i][j] dp1[i][j]和 d p 2 [ i ] [ j ] dp2[i][j] dp2[i][j] 分别表示 A A A 的分数为 i i i , B B B的分数为 j j j , A A A 先手和 B B B 先手的答案。如果 i i i 和 j j j 都小于等于 20 20 20, 它们的值都是固定的。刚好样例也给出了答案,直接抄过来就行。
其他情况下的转移方程如下:
d p 1 [ i ] [ j ] + = ( 1 − d p 2 [ g e t v ( i , x ) ] [ j ] ) / 20 ; dp1[i][j]+=(1-dp2[getv(i,x)][j])/20; dp1[i][j]+=(1−dp2[getv(i,x)][j])/20;
d p 2 [ i ] [ j ] = m a x ( d p 2 [ i ] [ j ] , ( 1 − d p 1 [ i ] [ g e t v ( j , x ) ] + 1 − d p 1 [ i ] [ g e t v ( j , y ) ] + 1 − d p 1 [ i ] [ g e t v ( j , z ) ] ) / 3 ) ; dp2[i][j]=max(dp2[i][j],(1-dp1[i][getv(j,x)]+1-dp1[i][getv(j,y)]+1-dp1[i][getv(j,z)])/3); dp2[i][j]=max(dp2[i][j],(1−dp1[i][getv(j,x)]+1−dp1[i][getv(j,y)]+1−dp1[i][getv(j,z)])/3);
其中 g e t v ( ) getv() getv() 函数就是判断能不能减去 x x x,具体代码如下:
int getv(int x,int y){
if(y<=x) return x-y;
return x;
}
注意到 d p 1 [ i ] [ j ] dp1[i][j] dp1[i][j] 可能会从 d p 2 [ i ] [ j ] dp2[i][j] dp2[i][j] 转移过来,同样 d p 2 [ i ] [ j ] dp2[i][j] dp2[i][j] 可能会从 d p 1 [ i ] [ j ] dp1[i][j] dp1[i][j] 转移过来,观察一下就可以发现分类讨论一下即可:
如果 i i i 和 j j j 都小于等于 20 20 20,答案前面说了是固定的值。
如果 i i i 小于等于 20 20 20,那么 j j j 必然大于 20 20 20,也就是说此时 d p 2 [ i ] [ j ] dp2[i][j] dp2[i][j] 不会从 d p 1 [ i ] [ j ] dp1[i][j] dp1[i][j] 转移过来,只要先转移 d p 2 dp2 dp2 即可。反之亦然。
#include
using namespace std;
typedef long long ll;
typedef double db;
const int N=502;
const db inf=1e12;
const db eps=1e-7;
db dp1[N][N],dp2[N][N];
int val[20]={20,1,18,4,13,6,10,15,2,17,3,19,7,16,8,11,14,9,12,5};
int getv(int x,int y){
if(y<=x) return x-y;
return x;
}
int main(){
for(int i=1;i<=20;i++){
for(int j=1;j<=20;j++){
dp1[i][j]=0.136363636364;
dp2[i][j]=0.909090909091;
}
}
for(int i=1;i<N;i++){
for(int j=1;j<N;j++){
if(i<=20&&j<=20) continue;
if(i<=20){
for(int x=0;x<20;x++){
int y=(x+1)%20,z=(x+2)%20;
dp2[i][j]=max(dp2[i][j],(1.0-dp1[i][getv(j,val[x])]+1.0-dp1[i][getv(j,val[y])]+1.0-dp1[i][getv(j,val[z])])/3.0);
}
for(int x=0;x<20;x++){
int y=(x+1)%20,z=(x+2)%20;
dp1[i][j]+=(1.0-dp2[getv(i,val[x])][j])/20.0;
}
}
else{
for(int x=0;x<20;x++){
int y=(x+1)%20,z=(x+2)%20;
dp1[i][j]+=(1.0-dp2[getv(i,val[x])][j])/20.0;
}
for(int x=0;x<20;x++){
int y=(x+1)%20,z=(x+2)%20;
dp2[i][j]=max(dp2[i][j],(1.0-dp1[i][getv(j,val[x])]+1.0-dp1[i][getv(j,val[y])]+1.0-dp1[i][getv(j,val[z])])/3.0);
}
}
}
}
int n;
while(scanf("%d",&n)){
if(n==0) break;
printf("%.10f %.10f\n",dp1[n][n],dp2[n][n]);
}
}