読者です 読者をやめる 読者になる 読者になる

Lilliput Steps

小さな一歩から着実に. 数学やプログラミングのことを書きます.

Typical DP Contest C - トーナメント

問題文 : トーナメント


解法 :

dp[i][x] := i 番目の人がx 回目の試合で勝てる確率, とし$S$ をx 回目の試合で当たる可能性のある人達の集合とすると
$dp[i][0] = 1.0$
$dp[i][x] = dp[i][x - 1] \times \displaystyle \sum_{j \in S} (dp[j][x - 1] \times \mathrm{Pr}[人j に勝てる]$)

となるので, 順次求めて$dp[x][k]$ を出力すれば良い. $O(k2^k)$ 時間で求まる.

コード :

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <cassert>
#include <vector>
#include <algorithm>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <list>
#include <set>
#include <map>

using namespace std;

double dp[2048][11];

int main()
{
	int k;
	int r[2048];
	
	scanf("%d", &k);
	
	for (int i = 0; i < 1 << k; i++){
		scanf("%d", &r[i]);
		dp[i][0] = 1;
	}
	
	for (int i = 1; i <= k; i++){
		for (int j = 0; j < 1 << k; j++){
			dp[j][i] = 0;
			int low = (j / (1 << i)) * (1 << i);
			int up = low + (1 << i);
			int low2 = (j / (1 << (i - 1))) * (1 << (i - 1));
			int up2 = low2 + (1 << (i - 1));
			for (int L = low; L < up; L++){
				if (low2 <= L && L < up2) continue;
				dp[j][i] += dp[L][i - 1] * (1.0 / (1 + pow(10, (r[L] - r[j]) / 400.0)));
			}
			dp[j][i] *= dp[j][i - 1];
		}
	}
	for (int i = 0; i < 1 << k; i++) printf("%lf\n", dp[i][k]);
	
	return (0);
}