kmjp's blog

競技プログラミング参加記です

yukicoder : No.108 トリプルカードコンプ

Code Festival 上海の問題と近かったね。
http://yukicoder.me/problems/121

問題

N種類のカードからなるカードゲームがある。
初期状態で各種類のカードをA[i]枚持っている。

1枚カードを購入すると、N種類のいずれかのカードが等確率で1枚ランダムで手に入る。

各カードを3枚以上にしたい。
購入するカード枚数の期待値を答えよ。

解法

あと3枚・2枚・1枚入手しないといけないカードの種類の数をn3,n2,n1とする。
状態(n3,n2,n1)に対してDPもしくはメモ化再帰すればよい。

この類の問題で良くある「1枚カード引いても有効な(もっと必要な)カードが来ない」という場合がある。
この場合、有効なのが来るまでカードを引く期待値は、有効なカードを引く確率の逆数になる。

int N,NN[3];
int A[101];

double memo[101][101][101];

double dpdp(int n3,int n2,int n1) {
	if(memo[n3][n2][n1]>=0) return memo[n3][n2][n1];
	
	memo[n3][n2][n1]=N*1.0/(n1+n2+n3);
	if(n1>0) memo[n3][n2][n1]+=dpdp(n3,n2,n1-1)*n1/(n1+n2+n3);
	if(n2>0) memo[n3][n2][n1]+=dpdp(n3,n2-1,n1+1)*n2/(n1+n2+n3);
	if(n3>0) memo[n3][n2][n1]+=dpdp(n3-1,n2+1,n1)*n3/(n1+n2+n3);
	return memo[n3][n2][n1];
}

void solve() {
	int i,j,k,l,r,x,y; string s;
	cin>>N;
	
	FOR(i,N) {
		cin>>A[i];
		if(A[i]==0) NN[2]++;
		if(A[i]==1) NN[1]++;
		if(A[i]==2) NN[0]++;
	}
	FOR(x,101) FOR(y,101) FOR(i,101) memo[x][y][i]=-1;
	memo[0][0][0]=0;
	_P("%.12lf\n",dpdp(NN[2],NN[1],NN[0]));
}

まとめ

★3つなので記事作成してみました。