UVa 11500 : Vampires
UVa演習 2014/7/14 (月) 問2
問題リンク : http://uva.onlinejudge.org/external/115/11500.html
問題概要:
2人のプレイヤーでゲームを行う
プレイヤー1の体力はEV1,プレイヤー2の体力はEV2である
ゲームがはじまるとプレイヤーが交互にサイコロをふっていく
サイコロのでた目がAT以下であればプレイヤー1の勝利
それ以外であればプレイヤー2の勝利
勝利したプレイヤーは負けたプレイヤーからDだけ体力を吸収する
つまり、プレイヤー1が勝利した場合
EV1 => EV1 + D
EV2 => EV2 - D
となる
体力が0以下になったプレイヤーがでたらゲームが終わる
プレイヤー1が勝利する確率をもとめよ
解法:
メモ化再帰 or ギャンブラー破産問題の公式を利用
各プレイヤーの体力、その時の確率、深さをもってメモ化再帰をして通った
確率がEPS以下になったら0をリターンするのだが、EPSは1e-270くらいにしないとうまくいかない
ギャンブラー破産問題 : Gambler's ruin - Wikipedia, the free encyclopedia
上の公式に当てはめると簡単に答えがでる
今回体力はDだけ移動するので、各プレイヤーの体力をDで割ること
コード:
#include<bits/stdc++.h> #define REP(i,s,n) for(int i=s;i<n;i++) #define rep(i,n) REP(i,0,n) #define EPS (1e-320) #define equals(a,b) (fabs((a)-(b))<EPS) using namespace std; const int MAX_D = 2000; double memo[30][30][MAX_D]; int AT,D; double P; double dfs(int EV1,int EV2,double tmp,int depth){ if( equals(tmp,0) ) return tmp; if( EV1 <= 0 || EV2 <= 0 ) { if( EV2 <= 0 ) return tmp; return 0; } //assert( depth < MAX_D ); if( depth < MAX_D && memo[EV1][EV2][depth] >= 0.0 ) return memo[EV1][EV2][depth]; double ret = dfs(EV1+D,EV2-D,tmp*P,depth+1); ret += dfs(EV1-D,EV2+D,tmp*(1-P),depth+1); if( depth < MAX_D ) memo[EV1][EV2][depth] = ret; return ret; } /* Gambler 1 が勝利する確率 n1 : ギャンブラー1が最初に所持している金額 n2 : ギャンブラー2が最初に所持している金額 p : ギャンブラー1が勝利する確率 D : 勝利によって移動する金額 */ double GamblersRuin(double n1,double n2,double p,double D){ n1 = ceil(n1/D), n2 = ceil(n2/D); double q = 1 - p; if( equals(p,0.5) ) return n1 / ( n1 + n2 ); else return (1.0-pow(q/p,n1))/(double)(1.0-pow(q/p,(int)(n1+n2))); } int main(){ int EV1,EV2; while( 1 ){ scanf("%d %d %d %d",&EV1,&EV2,&AT,&D); if( EV1 == 0 && EV2 == 0 && AT == 0 && D == 0 ) break; double p = (double) AT / 6.0; printf("%.1f\n",GamblersRuin(EV1,EV2,p,D)*100); /* rep(i,EV1+EV2+1) rep(j,EV1+EV2+1) rep(k,MAX_D) memo[i][j][k] = -1; P = (double)AT/6.0; // a probability that Vampire 1 wins a combat printf("%.1lf\n",dfs(EV1,EV2,1,0)*100.0); */ } return 0; }