L2-047 锦标赛

L2-047 锦标赛

2 k 2^{k} 2k名选手将要参加一场锦标赛。锦标赛共有 k 轮,其中第 i 轮的比赛共有 2 k − i 2^{k-i} 2ki
场,每场比赛恰有两名选手参加并从中产生一名胜者。每场比赛的安排如下:

对于第 1 轮的第 j 场比赛,由第 ( 2 j − 1 ) (2j−1) (2j1)名选手对抗第 2 j 2j 2j 名选手。
对于第 i 轮的第 j 场比赛(i>1),由第 (i−1) 轮第 ( 2 j − 1 2j−1 2j1) 场比赛的胜者对抗第 (i−1) 轮第 2 j 2j 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
​。在一场比赛中,若两名选手的能力值不同,则能力值较大的选手一定会打败能力值较小的选手;若两名选手的能力值相同,则两名选手都有可能成为胜者。
令 L(i,j)表示第 i 轮第 j 场比赛 败者 的能力值,令 w 表示整个锦标赛最终胜者的能力值。给定所有满足 1≤i≤k 且 1≤j≤ 2 k − i 2^{k−i} 2ki 的 L(i,j) 以及 w,请还原出 a1,a2,⋯,an。

输入格式:

第一行输入一个整数 k(1≤k≤18)表示锦标赛的轮数。
对于接下来 k 行,第 i 行输入 2k−i个整数 L(i,1),L(i,2),⋯,L(i, 2 k − i 2^{k−i} 2ki) (1≤L(i,j) ≤109),其中 L(i,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

首先读题,输入k轮比赛的败者,和最终胜者反过来输出最开始的选手的次序。初看,毫无头绪。仔细思考,题意是让我们将第k轮的败者与这一轮对决出来的胜者安排前后顺序,该轮的胜者全来自于k轮之后决出来的所有败者加上最终的胜者,逐步得到最开始的选手次序。那么应该如何安排前后次序呢?我们可以构造一个满二叉树,每一轮中对决的一对选手视为一对兄弟节点,因为他们俩都是从之前的对决中胜出的,也就是说他们都是以自身为根的树的最大值。故只要是满足这一个条件的顺序即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
//用于标记该子树的最大值
int signal(vector<int> &tree,LL index,vector<int> &maxtree) {
	int result=tree[index];
	if(index*2+1>=tree.size()){
		maxtree[index]=result;
		return result;
	}
	result=max(result,signal(tree,index*2+1,maxtree));
	result=max(result,signal(tree,index*2+2,maxtree));
	maxtree[index]=result;
	return result;
}
int main() {
	int k,h;
	cin>>k;
	h=k+1;//满二叉树的高度
	long long len1=1<<h-1;//位运算实现pow(2,h-1),

	vector<int> tree(len1*2-1,INT_MIN),maxtree(len1*2-1);//tree为满二叉树数组,maxtree为标记数组
	for(int i=0; i<k; i++) {
		for(int j=0; j<len1; j+=2) {
			cin>>tree[len1-1+j];//最开始将所有的结点放置在左孩子
		}
		len1/=2;
	}
	cin>>tree[0];//输入最终胜者
	signal(tree,0,maxtree);//标记出每个子树的最大值
	if(tree[0]<maxtree[0]){//如果最终胜者的值要小于满二叉树的最大值,则不可能存在
		cout<<"No Solution"<<endl;
		return 0;
	}
	tree[2]=tree[0];//先将最终胜者放置在亚军后面
	LL len2=1;
	for(int i=2; i<=h-1; i++) {
		len2*=2;
		for(int j=len2-1; j<2*len2-1; j+=2) {//遍历该轮每一对对决
			if(tree[j]>=maxtree[j]&&tree[j+1]>=maxtree[j+1]){//如果满足条件
				continue;
			}
			swap(tree[j],tree[j+1]);//不符合条件则交换位置
			if(!(tree[j]>=maxtree[j]&&tree[j+1]>=maxtree[j+1])){//依旧不符合条件则说明无解
				cout<<"No Solution"<<endl;
				return 0;
			}
		}
		for(int j=len2-1; j<2*len2-1; j++){//将该轮对决者全部安插在前一轮的右节点,因为该轮对决者就是前一轮的所有胜者
			if(j*2+2<tree.size()){
				tree[j*2+2]=tree[j];
			}
		}
	}
	for(int i=len2*2-1;i<tree.size();i++){//输出答案
		if(i==tree.size()-1){
			cout<<tree[i]<<endl;
		}else{
			cout<<tree[i]<<" ";
		}
	}
	return 0;
}

有的同学可能会想,如果a,b对决,ab和ba都可以,那么我是不是两种情况都要考虑.其实是一样的,如果ab和ba都可以,说明a和b都大于左右子树的最大值,那么,他们俩谁前谁后对于前些轮的对决次序是没有影响的,所有我这里如果ab这种次序可以那么久直接continue了。注意我在这里是使用了maxtree数组来标记最大值,因为如果你每次判断ab或者ba那种符合条件,便就要遍历一次数组这样时间复杂度为 O ( n 3 ) O(n^{3}) O(n3),超时了,所以我选择以空间换时间,一次遍历完数组记录最大值。

小结

看到这里就算结束了。首先题目很长,对于同学们的阅读理解能力很有考究,比赛时能耐下性子来读题还是很不错的,其次还得想到用二叉树数组来存储每轮对决情况,通过抓住题眼(每一对选手都是对应子树的最大值)来安排次序。总的来说,这道题还是非常有难度的。如果还是有不懂的同学,可以在评论区里问出来,我看到的话会回复你的,也希望如果你看懂了的话,帮忙给评论区里不懂的同学解释一下,给别人解释也能帮助自己更加深刻理解这道题。好了,我是白丁,感谢阅读。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值