NOI导刊 2010 提高 淘汰赛制
题目:
题目描述
淘汰赛制是一种极其残酷的比赛制度。(2n)名选手分别标号(1,2,3,…,2^{n}-1,2^{n}),他们将要参加(n)轮的激烈角逐。每一轮中,将所有参加该轮的选手按标号从小到大排序后,第(1)位与第(2)位比赛,第(3)位与第(4)位比赛,第(5)位与第(6)位比赛……只有每场比赛的胜者才有机会参加下一轮的比赛(不会有平局)。这样,每轮将淘汰一半的选手。(n)轮过后,只剩下一名选手,该选手即为最终的冠军。现在已知每位选手分别与其他选手比赛获胜的概率,请你预测一下谁夺冠的概率最大。
输入输出格式
输入格式:
第一行是一个整数(n(l leqslant n leqslant 10)),表示总轮数。接下来(2^{n})行,每行(2^{n})个整数,第(i)行第(j)个是(P_{i j}(0≤P_{i j}≤100,P_{ii}=0,P_{i j}+P_{j i}=100)),表示第(i)号选手与第(j)号选手比赛获胜的概率。
输出格式:只有一个整数(c),表示夺冠概率最大的选手编号(若有多位选手,输出编号最小者)。
输入输出样例
输入样例#1:
2 0 90 50 50 10 0 10 10 50 90 0 50 50 90 50 0
输出样例#1:
1
说明
(30%)的数据满足(n leqslant 3);(100%)的数据满足(n leqslant 10)。
题解:
为表达简洁,我还是设几个值吧:
设(p[i][j])为(j)号选手第(i)轮晋级的概率;(m[i][j])为(i)与(j)比赛的胜利概率。
分组,每一轮求出有可能与(i)进行比赛的选手。设(l)与(r)为有可能与(i)比赛选手的序列的左区间与右区间,(q)为当前比赛轮数,求(sum = sum_{k = l}^{r} p[q - 1][k] * m[i][k]),(p[q][i])即为(p[q - 1][i] * sum)。
#include <cstdio>
const int MAXN = 1100;
const double EPS = 1e-6;
int m[MAXN][MAXN];
double p[12][MAXN];
int n, tn, ansi;
int main() {
scanf("%d", &n);
tn = 1 << n;
for (int i = 1; i <= tn; ++i)
for (int j = 1; j <= tn; ++j)
scanf("%d", &m[i][j]);
for (int i = 1; i <= tn; ++i) p[0][i] = 1.0; //因为第零轮肯定没有人淘汰,所以均为1.0
for (int i = 1; i <= n; ++i) {
int rnum = 1 << (i - 1);
for (int j = 1; j <= tn; ++j) {
int numt(0), numc(0);
numt = (j - 1) / rnum + 1; //计算i号选手的当前组数
numc = (numt & 1) ? numt + 1 : numt - 1;
int rangex = rnum * numc; double sum(0);
for (int k = rnum * (numc - 1) + 1; k <= rangex; ++k) //得到有可能与其比赛的选手范围
sum = sum + p[i - 1][k] * m[j][k] * 0.01;
p[i][j] = p[i - 1][j] * sum;
}
}
ansi = 1;
for (int i = 2; i <= tn; ++i)
if(p[n][i] - p[n][ansi] > EPS) ansi = i;
printf("%d
", ansi);
return 0;
}