2023团队程序设计天梯赛--L2-3 锦标赛 题解

文章讲述了如何通过给定每轮比赛败者的最低能力值和最终胜者的值,利用树形结构和递归方法还原所有选手的能力值序列。如果存在多种合法解,输出任意一种;否则,如果无法找到满足条件的解,则输出NoSolution。
摘要由CSDN通过智能技术生成

题目一览

有 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;
}

  • 27
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值