kmjp's blog

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

TopCoder SRM 752 Div1 Medium Literature

解けはしたけど手間取った。
https://community.topcoder.com/stat?c=problem_statement&pm=15326

問題

1~3Nのカードを、3人でN枚ずつ持っている。
1番手は、自身のカードをすべて知っているが、他の2人が何を持っているかは知らない。
ここで、3人が順に自分が持っていないカード2N枚のうちどれかの番号を等確率でしゃべる(同じものを複数回話すこともある)。
今、3人のしゃべった履歴が与えられている。

この状態で、1番手が全員のカードを全部認識するには、3人合計で何回しゃべった後か、その期待値をこたえよ。

解法

1人目のカードはわかっているので、2人目の発言は3人目の、3人目の発言は2人目の未確定カードを言い当てる可能性がある。
f(A,B) := 次に2人目がしゃべる番であるとき、2人目の未確定カードがA枚、3人目の未確定カードがB枚とする場合で、1番手が全員のカードを認識するまでの期待値
とする。これを頑張って求めよう。

さて、この類の問題で、「1枚引いて有効なカードが来る確率がpの時、期待値1/p枚で有効なカードが来て次の状態に進む」という定番テクがある。
ただ今回は3人でカードを引いているので、少しややこしい。
2人目と3人目が一気にカードを引いたとする。
少なくとも片方が相手の未確定カードを引く確率をpとすると、有効カードを引くまで3/p-3枚かかる。
あとは

  • 2人目が3人目の未確定カードを引き、3人目は2人目の未確定カードを引き当てなかった
  • 3人目が2人目の未確定カードを引き、2人目は3人目の未確定カードを引き当てなかった
  • ともに相手の未確定カードを引き当てた

場合の期待値を足そう。

class Literature {
	public:
	int N;
	double hoge(int A,int B) {
		assert(A && B);
		if(memo[A][B]>=0) return memo[A][B];
		double ret=0;
		double add=3*(2*N)*(2*N)*1.0/(2*N*(A+B)-A*B)-3;
		
		double AB=1.0*A*B/(2*N*(A+B)-A*B);
		double Ab=1.0*A*(2*N-B)/(2*N*(A+B)-A*B);
		double aB=1.0*(2*N-A)*B/(2*N*(A+B)-A*B);
		
		// oo
		if(A==1 && B==1) {
			ret+=(AB+Ab)*(add+1);
			ret+=aB*(add+2);
		}
		else if(A==1) {
			ret+=(AB+Ab)*(add+1);
			ret+=aB*(add+3+hoge(A,B-1));
		}
		else if(B==1) {
			ret+=(AB+aB)*(add+2);
			ret+=Ab*(add+3+hoge(A-1,B));
		}
		else {
			ret+=AB*(add+3+hoge(A-1,B-1));
			ret+=Ab*(add+3+hoge(A-1,B));
			ret+=aB*(add+3+hoge(A,B-1));
		}
		
		
		return memo[A][B]=ret;
	}
	
	double expectation(int n, vector <int> Teja, vector <int> history) {
		int did[3030]={};
		N=n;
		int lef[2]={N,N};
		FORR(a,Teja) did[a]=1;
		int i;
		FOR(i,history.size()) {
			if(did[history[i]]) continue;
			if(i%3==0) continue;
			did[history[i]]=1;
			if(i%3==1) lef[1]--;
			if(i%3==2) lef[0]--;
			if(lef[0]==0 || lef[1]==0) return i+1;
		}
		int cur=history.size();
		
		int x,y;
		FOR(x,1010) FOR(y,1010) memo[x][y]=-1;
		
		
		
		if(cur%3==0) cur++;
		
		if(cur%3==1) return cur+hoge(lef[1],lef[0]);
		else return cur+lef[0]*1.0/(2*N)*(lef[0]==1?1:(2+hoge(lef[1],lef[0]-1)))+(2*N-lef[0])*1.0/(2*N)*(2+hoge(lef[1],lef[0]));
	}
}

まとめ

状態遷移がややこしかった。
1発で通ったけど、結構時間がかかったので本番出てたら結構苦戦してそう。