题目一览
有 2k 名选手将要参加一场锦标赛。锦标赛共有 k 轮,其中第 i 轮的比赛共有 2k−i 场,每场比赛恰有两名选手参加并从中产生一名胜者。每场比赛的安排如下:
- 对于第 1 轮的第 j 场比赛,由第 (2j−1) 名选手对抗第 2j 名选手。
- 对于第 i 轮的第 j 场比赛(i>1),由第 (i−1) 轮第 (2j−1) 场比赛的胜者对抗第 (i−1) 轮第 2j 场比赛的胜者。
第 k 轮唯一一场比赛的胜者就是整个锦标赛的最终胜者。
举个例子,假如共有 8 名选手参加锦标赛,则比赛的安排如下:
- 第 1 轮共 4 场比赛:选手 1 vs 选手 2,选手 3 vs 选手 4,选手 5 vs 选手 6,选手 7 vs 选手 8。
- 第 2 轮共 2 场比赛:第 1 轮第 1 场的胜者 vs 第 1 轮第 2 场的胜者,第 1 轮第 3 场的胜者 vs 第 1 轮第 4 场的胜者。
- 第 3 轮共 1 场比赛:第 2 轮第 1 场的胜者 vs 第 2 轮第 2 场的胜者。
已知每一名选手都有一个能力值,其中第 i 名选手的能力值为 ai。在一场比赛中,若两名选手的能力值不同,则能力值较大的选手一定会打败能力值较小的选手;若两名选手的能力值相同,则两名选手都有可能成为胜者。
令 li,j 表示第 i 轮第 j 场比赛 败者 的能力值,令 w 表示整个锦标赛最终胜者的能力值。给定所有满足 1≤i≤k 且 1≤j≤2k−i 的 li,j 以及 w,请还原出 a1,a2,⋯,an。
输入格式:
第一行输入一个整数 k(1≤k≤18)表示锦标赛的轮数。
对于接下来 k 行,第 i 行输入 2k−i 个整数 li,1,li,2,⋯,li,2k−i(1≤li,j≤109),其中 li,j 表示第 i 轮第 j 场比赛 败者 的能力值。
接下来一行输入一个整数 w(1≤w≤109)表示锦标赛最终胜者的能力值。输出格式:
输出一行 n 个由单个空格分隔的整数 a1,a2,⋯,an,其中 ai 表示第 i 名选手的能力值。如果有多种合法答案,请输出任意一种。如果无法还原出能够满足输入数据的答案,输出一行
No Solution
。
请勿在行末输出多余空格。输入样例1:
3 4 5 8 5 7 6 8 9
输出样例1:
7 4 8 5 9 8 6 5
输入样例2:
2 5 8 3 9
输出样例2:
No Solution
提示:
本题返回结果若为格式错误均可视为答案错误。
大体思路
题目的描述有点抽象,但是举例说的很清楚了
1轮 1v2 3v4 5v6 7v8
2轮 1v2 3v4
3轮 1v2
这里i v j 的 i和j 指的是上一轮第i场的获胜者vs上一轮第j场的获胜者
不难发现这个 i v j 是呈树形结构的
以样例1为例,我们可以画出对应的树
黑色代表能力值,红色代表编号
现在给出若干个能力值,包括最终获胜的能力值以及每轮每场的败者值,要求还原原序列
也就是从树根往下递归到叶子结点
每次只需要拿上一层传下来的父结点跟当前的子结点比较
在一场比赛中,若两名选手的能力值不同,则能力值较大的选手一定会打败能力值较小的选手;若两名选手的能力值相同,则两名选手都有可能成为胜者。
如果父结点的能力值 < 当前子结点的能力值,那就是不合法的,递归结束
否则
1.还没到最后一层,也就是叶结点,继续往下递归
2.到了叶结点,记录合法序列,如果到了最后一个叶结点,就拷给最终答案序列
递归成功的条件是什么呢?
看样例1第二层的时候,我们当前的结点有8 和 9,下一层有两个子结点 7 和 6,
我们可以选择 8传到7、9传到6,也可以选择8传到6、9传到7,只要有一种情况满足即可
这对于每一层来说都是这样的
思路搞清楚以后,其实稍微麻烦一点的是编号输入和合法序列的记录
编号输入
3 4 5 8 5 7 6 8 9
输入是能力值是从叶子结点开始给的
第一行给n
第二行我们希望的编号是 5 6 7 8
第三行我们希望的编号是 3 4
第四行是 2
第五行是 1
除了根结点以外,每一行其实就是从 2^(i - 1) + 1到 2^i,i从n倒序枚举
例如第二行,i = n = 3,也就是2^(3-1) + 1 = 5 到 2^3 = 8
合法序列的记录
到了最后一层时,我们要记录合法序列,每次记录序列的两个位置,而且这两个位置是相邻连续的
以样例1为例,最后一层的编号是 5 6 7 8
最后的答案可以是 _ 5 _ 6 _ 7 _ 8 ,空格就是传下来的父结点要填入的位置
我们可以统一把叶结点映射成 1,3 ,5, 7,....
然后传下来的父结点的位置就是叶结点的位置 + 1 ,也就是 2,4,6,8,.....
怎么映射呢?
可以将所有叶结点的编号减去 2 ^ (n - 1) ,然后乘以2 再 减 1
比如 6 - 2 ^(3 - 1) = 2, 2 * 2 = 4, 4 - 1 = 3
具体代码
#include<iostream>
#include<vector>
using namespace std;
const int N = 3e5 + 10;
int n;
vector<int> a(N);
vector<int> tmp(N),ans(N);
bool dfs(int u,int f){
if(a[f] < a[u]) return false;
if(u > (1 << n - 1)){
int idx = u - (1 << n - 1);
idx += idx - 1;
tmp[idx] = a[u];
tmp[idx + 1] = a[f];
if(u == (1 << n)) ans = tmp;
return true;
}
return (dfs(2 * u - 1,u) && dfs(2 * u,f)) || (dfs(2 * u - 1,f) && dfs(2 * u,u));
}
int main(){
cin >> n;
for(int i = n; i >= 1; --i)
for(int j = (1 << i - 1) + 1; j <= (1 << i); ++j)
cin >> a[j];
cin >> a[1];
if(!dfs(2,1)) cout << "No Solution";
else{
for(int i = 1; i <= (1 << n); ++i) cout << ans[i] << " ";
}
return 0;
}