【概率DP】Gym - 102470 - D - Darts

题目链接http://codeforces.com/gym/102470/problem/D


D - Darts


题意

A A A B B B 两个人比赛射飞镖,规则如下:

两个人轮流射飞镖,初始分数都为 N N N,假设射中区域 x x x,如果 x ≤ N x\le N xN,分数减去 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 1N501

标靶权值为: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, 它们的值都是固定的。刚好样例也给出了答案,直接抄过来就行。

其他情况下的转移方程如下:

  • A A A 先手,射到权值为 x x x 的概率是 1 20 \frac{1}{20} 201。因为换人了所以因该是由 d p 2 [ i − x ] [ j ] dp2[i-x][j] dp2[ix][j] 转移过来,又因为 d p 2 dp2 dp2 表示 B B B 获胜的概率,所以要用 1 1 1 减去它:

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]+=(1dp2[getv(i,x)][j])/20;

  • B B B 先手,分别射三个区域 x , y , z x,y,z x,y,z,转移方程如下:

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],(1dp1[i][getv(j,x)]+1dp1[i][getv(j,y)]+1dp1[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]);
    }
}

你可能感兴趣的:(动态规划)