POJ练习题之:败方树

问题描述

给定一个整数数组,要求对数组中的元素构建败方树(数组相邻元素两两比较,从第一个元素开始)。之后修改数组中的元素,要求输出初始构建以及修改后得到的败方树的所有内部结点代表的整数(从左到右从上到下输出)

输入
第一行为数组的元素个数n和修改的次数m。
第二行为n个整数,即数组的元素。
接下来m行代表m次修改操作,每次操作修改数组中的一个元素,每一行包括两个整数,第一个为被修改元素在数组中的标号,第二个为修改之后的元素值。
输出
输出m+1行。
第一行为初始构建的败方树的所有内部结点代表的整数(按照树的结点从左到右从上到下的顺序输出)
接下来m行为接下来m次修改后得到的败方树的所有内部结点代表的整数(按照树的结点从左到右从上到下的顺序输出)
样例输入
8 1
10 9 20 6 16 12 90 17
3 15
样例输出
6 12 9 17 10 20 16 90
9 12 15 17 10 20 16 90

该题总体思路比较直观,就是模拟创建败者树的过程
总共就是初始建树和修改两个步骤
由于在败者树当中想要建立上一层的根节点需要知道赢者,而找到赢者需要回溯底层节点
观察到有的博客的建树思路是在每一次建立一个节点都向上一直更新到根节点(这样就不需要去做向下回溯寻找赢者的步骤了)
但是这样子建树第一遍是不完备的(受到顺序的影响),所以需要再做一遍
如下的代码思路比较直观,寻找赢者节点是直接使用的递归
POJ上的测试数据并不大,基本上不管使用什么思路只要是正确的应该不会存在超时或者爆栈 的问题。
整体代码由于是利用数组指针对败者树进行模拟,所以指针位置的操作比较繁琐,略显复杂,使部分代码不是很直观

#include<iostream>
using namespace std;
int A[1001];//将数组设为全局变量更有利于操作 
int n,m;//同理设为全局变量 
class Loser_tree{//创建败者树类,所有函数均在类中定义 
	public:
	int len;//败者树数组的长度 
	int Max_N;//树的最底层元素的个数 
	int floor;//败者树的层数(除去赢者节点Tree_Array[0]) 
	int *Tree_Array;//败者树数组指针 
	void Build(int );//初始建树函数 
	Loser_tree(int );//构造函数 
	int Vers(int pos);//寻找赢者(利用递归从当前根节点向下) 
	void ReBuild(int ,int );//修改败者树之后的重建 
};
Loser_tree::Loser_tree(int n){
	len=0;
	floor=0;
	Max_N=n/2;
	if(n%2)++Max_N;//根据奇偶确定败者树最底层元素个数 
	for(int s=Max_N;s>=1;s/=2) ++floor,len+=s;//确定层数和数组长度 
	Tree_Array=new int[len+1];//
	Build(n); //建树 
	
}
void Loser_tree::Build(int n){
	int father;
	int l=n%2 ? n-1:n;//
	for(int i=0,j=1;i<l;i+=2,++j){//先将最底层建立 
		father=len-Max_N+j;
		Tree_Array[father]=A[i]>A[i+1] ? i:i+1;
	}
	if(n%2) Tree_Array[len]=A[n-1];//若为奇数则树最后节点补充 
	int sum=len-Max_N;
	int a,b;
	for(int s=1,j=2;s<floor;++s,j*=2){//从最底层逐层向上 
		sum-=Max_N/j;
		for(father=sum+1;father<=sum+Max_N/j;++father){
			a=Vers(2*father);//
			b=Vers(2*father+1);//寻找赢者 
			Tree_Array[father]=A[a]>A[b] ? a:b;
		}
	}
	Tree_Array[0]=Vers(1);
}
int Loser_tree::Vers(int pos){
	if(pos>len-Max_N){//若到达最底层 
	if(Tree_Array[pos]==n-1&&Tree_Array[pos]%2==0) return n-1;
	else return Tree_Array[pos]%2 ? Tree_Array[pos]-1 :Tree_Array[pos]+1;
	}
	else {//若不在最底层则向下递归 
		int a,b;
		a=Vers(2*pos);
		b=Vers(2*pos+1);
		return A[a]<A[b]? a:b;
	}
}
void Loser_tree::ReBuild(int pos,int val){//重建 
	int a=pos%2 ? pos-1:pos;
	int b=pos%2 ? pos:pos+1;
	int father=len-Max_N+(pos+2)/2;
	Tree_Array[father]=A[a]>A[b] ? a:b;
	father/=2;
	while(father>=1){
		a=Vers(2*father);
		b=Vers(2*father+1);
		Tree_Array[father]=A[a]>A[b] ? a:b;
		father/=2;
	}
//	cout<<endl;
	Tree_Array[0]=Vers(1);
}
int main(){

	cin>>n>>m;
	for(int i=0;i<n;++i)cin>>A[i];
	Loser_tree Tree(n);
	int len=Tree.len;
//	for(int i=0;i<n;++i) cout<<Tree.Tree_Array[i]<<" ";
//	cout<<endl;
	for(int i=0;i<=len;++i) cout<<A[Tree.Tree_Array[i]]<<" ";
	int pos,val;
	for(int i=0;i<m;++i){
		cout<<endl;
		cin>>pos>>val;
		A[pos]=val;
//		for(int i=0;i<n;++i)cout<<A[i]<<" ";
//		cout<<endl;
		Tree.ReBuild(pos,val);
//		for(int i=0;i<=len;++i) cout<<Tree.Tree_Array[i]<<" ";
//		cout<<endl;
		for(int i=0;i<=len;++i) cout<<A[Tree.Tree_Array[i]]<<" ";
	}
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值