题目描述
题目描述
有 名选手按一定的排列进行 轮一对一淘汰赛决出一名胜者,每次对抗均为相邻的两位选手。
每名选手都有一个能力值,能力值高的会击败能力值低的,相同能力值均有获胜可能。
现在给出每轮淘汰赛失败的选手的能力值,要求你还原一种可能的初始人员排列。
输入样例
3
4 5 8 5
7 6
8
9
输出样例
7 4 8 5 9 8 6 5
题目分析
由我看来,这是一道很有思维的模拟题,并同时含有二叉树的思想,由于一个错误的想法误导了我很久。
我们定义一个 res 数组用来表示初始排列,winer_id[i][j] 表示第 i 轮比赛的第 j 组的胜者在 res 中的下标, loser_val[i][j] 表示从第 i 轮比赛的第 j 组比赛开始,其所有的前继比赛(如果把整场赛事画出一幅晋级图,即一棵二叉树,每一个节点代表一场比赛,则某一场比赛的前继比赛为当前节点及其子树的所有节点)中,能力最大的败者的能力值。
从第一轮比赛开始看,我们知道无论将败者能力值赋给一场比赛的哪位选手,都不会影响最终结果。注意给 loser_val 数组赋值的同时,需要传递 winer_id 数组的选手代号。
从第二轮比赛开始,我们每次输入一个初始的 lower_val[i][j] 需要保证其至少需要大于当场比赛的左右子节点中的一个值,即 loser_val[i][j] > lower_val[i-1][j*2] || loser_val[i][j] > loser_val[i-1][j*2-1],其现实意义为当前比赛的败者至少也是其前继比赛中的最终获胜者,即能力大于前继比赛中的最强失败者。若判断无误后,需要利用其直接前继比赛的 winer_id[i-1][j+judge()] 为 res 数组的相应位置赋值,即将当前比赛的败者能力值赋给满足要求的一位选手的上一场比赛的胜者在 res 数组中的编号。
最终输入总获胜者能力值,只需要与 winer_val[k][1] 即最强失败者的能力比较是否合法并填到 res[winer_id[k][1]] 即可。
Accept代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6;
int loser_val[20][N], winer_id[20][N], res[N];
int main()
{
bool flg = true;
int k; cin >> k;
for (int i = 1; i <= k; i ++)
for (int j = 1; j <= 1 << k - i; j ++)
{
cin >> loser_val[i][j];
if (!flg) continue;
if (i == 1) res[j * 2] = loser_val[i][j], winer_id[i][j] = j * 2 - 1;
else
{
int l = j * 2 - 1, r = j * 2;
int mx = max({loser_val[i][j], loser_val[i - 1][l], loser_val[i - 1][r]});
if (loser_val[i][j] < loser_val[i - 1][l] && loser_val[i][j] < loser_val[i - 1][r]) flg = false;
else
{
if (loser_val[i][j] >= loser_val[i - 1][r])
{
res[winer_id[i - 1][r]] = loser_val[i][j];
winer_id[i][j] = winer_id[i - 1][l];
}
else
{
res[winer_id[i - 1][l]] = loser_val[i][j];
winer_id[i][j] = winer_id[i - 1][r];
}
loser_val[i][j] = mx;
}
}
}
int fin; cin >> fin;
if (fin < loser_val[k][1]) flg = false;
else res[winer_id[k][1]] = fin;
if (!flg) cout << "No Solution";
else for (int i = 1; i <= 1 << k; i ++) cout << res[i] << "\n "[i < 1 << k];
return 0;
}