うーん。
https://community.topcoder.com/stat?c=problem_statement&pm=14021
問題
トランプのブラックジャックを考える。
プレイヤーは初期に配られた2枚のカードを知っている。
ディーラーは2枚のカードを持っており、1枚はプレイヤーに知られているが1枚は不明である。
元々デッキのうち、既知の3枚のカードを除くとどんなカードがあるかプレイヤーは知っている。
プレイヤーがディーラーがデッキからカードを引くと、残されたカードのうち1枚が等確率で引くものとする。
プレイヤーが最適手を取ったとき、勝率を求めよ。
解法
デッキのカード枚数が高々16枚なのでbitmaskを用いてDFSする。
まずプレイヤーの手札と残されたカードでDFSし、そのつどもう1枚引くかもう引かないかを考えて勝率の上がる方を選べばよい。
double memo1[22][2][1<<16]; vector<double> memo2[22][2][1<<16]; class CardCounter { public: int deal; vector<int> D; vector<double> get_dealer(int sum,int ace,int mask) { vector<double> R(22,0); if(sum>21) { R[0]=1; } else if(sum+10*ace>=17 && sum+10*ace<=21) { R[sum+10*ace]=1; } else if(sum>=17 && sum<=21) { R[sum]=1; } else { if(memo2[sum][ace][mask].size()) return memo2[sum][ace][mask]; int num=__builtin_popcount(mask); int i,j; FOR(i,D.size()) if(mask&(1<<i)) { auto R2=get_dealer(sum+D[i],ace|(D[i]==1),mask^(1<<i)); FOR(j,22) R[j]+=R2[j]*1.0/num; } memo2[sum][ace][mask] = R; } int i; return R; } double hoge(int sum,int ace,int mask) { double end=0; if(sum>21) return 0; if(memo1[sum][ace][mask]<0) { vector<double> op=get_dealer(deal,deal==1,mask); int cur=sum; if(ace&&sum+10<=21) cur+=10; int i; FOR(i,cur) end+=op[i]; double take=0; FOR(i,D.size()) if(mask&(1<<i)) take += hoge(sum+D[i],ace | (D[i]==1),mask^(1<<i)); memo1[sum][ace][mask] = max(end,take/__builtin_popcount(mask)); } return memo1[sum][ace][mask]; } double winningChance(vector <int> deck, int dealer, vector <int> player) { deal = dealer; D.clear(); int i,j,k; FOR(i,deck.size()) while(deck[i]--) D.push_back(i+1); FOR(i,22) FOR(j,2) FOR(k,1<<16) memo1[i][j][k]=-1,memo2[i][j][k].clear(); return hoge(player[0]+player[1],(player[0]==1)|(player[1]==1),(1<<D.size())-1); } }
まとめ
面倒なだけであまり面白味もないし、まぁ400ptでしょうがないかな。