ZOJ 2334 Monkey King

该博客介绍了ZOJ 2334题目,涉及一个关于猴子冲突的森林场景。每只猴子有自己的强悍值,冲突导致双方朋友形成更大朋友圈,强悍值减半。每个测试案例包含猴子数量、冲突次数,需要求解冲突后的最强悍猴子的强悍值。题目链接和无注释代码可供参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:

        在一个森林里曾经有一群猴子(总共有N只,N ≤ 100,000),一开始它们各干各的,互不相识,但是猴子数量多不可避免地会发生冲突,这些冲突只会发生在互不相识的两个猴子之间。当冲突发生时,两只猴子各自都会招来自己最强悍的朋友来一场决斗(如果发生冲突的猴子本身就是自己朋友圈中最强悍的,则它会直接自己上),假设每个猴子都有一个强悍值(题中会指定各个猴子的强悍值),决斗之后,两只猴子的强悍值都降为原来的一半,并且双方的朋友也都相互结交成了朋友组成了一个更大的朋友圈。

        现有多个测例(测例数量无上限),每个测例中都会先给出猴子数量N(并赋予每个猴子一定的强悍值)和冲突数量M,对于每次冲突都会给出发生冲突的两个猴子的编号,如果两个猴子是相识的则输出-1,否则输出冲突后当事人圈子(合并后)的最强悍猴子的强悍值。

题目链接

注释代码:

/*                
 * Problem ID : ZOJ 2334 Monkey King
 * Author     : Lirx.t.Una                
 * Language   : C      
 * Run Time   : 380 ms                
 * Run Memory : 3684 KB                
*/   

#include <string.h>
#include <stdio.h>

//maximum number of monkeys
//猴子的最大数量
//100,000 + 1
//数组下标可以取到1-100,000
#define	MAXMONKEYN		100001

struct 	BNode;//Body of Node,左偏树结点

typedef	struct BNode		Node;
typedef	struct BNode *		Tree;

struct	BNode {

	int		key;//强悍值
	int		npl;//nil path length,
	      //零路径长度,即当前结点到最近
	      //叶子结点的距离(按边的数量算)

	Tree	lft;
	Tree	rht;
};

int		set[MAXMONKEYN];//并查集集合
Node	nod[MAXMONKEYN];//node,存放左偏树的各个结点
Tree	mok[MAXMONKEYN];//monkey,最开始每个猴子所代表的结点都是
                 //一棵树的树根

Tree
Merge( Tree t1, Tree t2 ) {//左偏树经典操作,合并两颗左偏树
	//也是堆的合并的经典操作

	if ( !t1 )
		return t2;

	if ( !t2 )
		return t1;

	Tree
	Mg_to_R( Tree t1, Tree t2 );//Merge to right child
	                     //将t2和t1的右子树进行合并

	//保持树顶一定是最大值
	if ( t1->key > t2->key )
		return Mg_to_R( t1, t2 );
	else
		return Mg_to_R( t2, t1 );
}

void
SwapCld(Tree tree) {//swap child
	    //将tree的两个子树进行交换
	
	Tree	ttmp;

	ttmp		= tree->lft;
	tree->lft	= tree->rht;
	tree->rht	= ttmp;
}

Tree
Mg_to_R( Tree t1, Tree t2 ) {

	//左偏树必须是左子树的npl长≥右子树的
	//因此当左子树为空的时候可以直接将t2接在
	//t1的左子树上
	if ( !t1->lft )
		t1->lft = t2;
	else {
	
		//否则先跟t1右子树合并(因为右子树比较短)
		//这样可以使整棵树趋于平衡
		t1->rht = Merge( t1->rht, t2 );

		//但如果过头了(即右子树偏大)
		//则需要交换两个子树了
		if ( t1->rht->npl > t1->lft->npl )
			SwapCld(t1);

		//由于左子树永远长于右子树
		//因此零路径长只与右子树有关
		//在右子树的npl上加1即可更新
		t1->npl = t1->rht->npl + 1;
	}

	return t1;
}

Tree
Adjst(Tree tree) {//Adjust,对两颗准备决斗的树
	    //在决斗之后进行调整
	//主要任务就是模拟决斗发生之后,树顶(最强悍猴子)
	//的强悍值减半,并挪去,将左右子树合并后再将踢出的
	//原树顶结点重新加入该树(其实也是合并操作)

	Tree	tmax;

	tmax	= tree;
	tree	= Merge( tree->lft, tree->rht );
	
	//被踢出后需要对其npl以及左右链域进行及时清空
	//否则会导致内存崩溃引发段错误而吐核
	tmax->key	>>= 1;
	tmax->npl	=	0;
	tmax->lft	= NULL;
	tmax->rht	= NULL;

	return Merge( tmax, tree );
}

int
find(int x) {

	if ( x == set[x] )
		return x;

	return set[x] = find( set[x] );
}

int
main() {

	int		nm;//number of monkeys
	int		nq;//number of quarrels
	int		i;//计数变量
	int		u, v;//发生争斗的两个猴子的编号
	int		su, sv;//set of u and v,求u、v所在的集合

	while ( ~scanf("%d", &nm) ) {
	
		//每个测例开始时的初始化
		for ( i = 1; i <= nm; i++ )
			set[i] = i;
		memset( nod + 1, 0, nm * sizeof(Node));

		//输入
		for ( i = 1; i <= nm; i++ )	{

			scanf("%d", &nod[i].key);
			mok[i] = nod + i;
		}
		scanf("%d", &nq);
		while ( nq-- ) {
		
			scanf("%d%d", &u, &v);
			
			su = find(u);
			sv = find(v);

			if ( su == sv ) {
			
				puts("-1");
				continue;
			}

			mok[su] = Adjst( mok[su] );
			mok[sv] = Adjst( mok[sv] );

			//由于合并后的树存于sv中
			//所以相对应的并查集合并也必须将集合合并在sv中
			//否则在下次find的时候找的结点不是左偏树的树顶
			mok[sv] = Merge( mok[su], mok[sv] );
			set[su] = sv;

			printf("%d\n", mok[sv]->key);
		}
	}

	return 0;
}

无注释代码:

#include <string.h>
#include <stdio.h>

#define	MAXMONKEYN		100001

struct 	BNode;

typedef	struct BNode		Node;
typedef	struct BNode *		Tree;

struct	BNode {

	int		key;
	int		npl;

	Tree	lft;
	Tree	rht;
};

int		set[MAXMONKEYN];
Node	nod[MAXMONKEYN];
Tree	mok[MAXMONKEYN];

Tree
Merge( Tree t1, Tree t2 ) {

	if ( !t1 )
		return t2;

	if ( !t2 )
		return t1;

	Tree
	Mg_to_R( Tree t1, Tree t2 );

	if ( t1->key > t2->key )
		return Mg_to_R( t1, t2 );
	else
		return Mg_to_R( t2, t1 );
}

void
SwapCld(Tree tree) {
	
	Tree	ttmp;

	ttmp		= tree->lft;
	tree->lft	= tree->rht;
	tree->rht	= ttmp;
}

Tree
Mg_to_R( Tree t1, Tree t2 ) {

	if ( !t1->lft )
		t1->lft = t2;
	else {
	
		t1->rht = Merge( t1->rht, t2 );
		if ( t1->rht->npl > t1->lft->npl )
			SwapCld(t1);

		t1->npl = t1->rht->npl + 1;
	}

	return t1;
}

Tree
Adjst(Tree tree) {

	Tree	tmax;

	tmax	= tree;
	tree	= Merge( tree->lft, tree->rht );
	
	tmax->key	>>= 1;
	tmax->npl	=	0;
	tmax->lft	= NULL;
	tmax->rht	= NULL;

	return Merge( tmax, tree );
}

int
find(int x) {

	if ( x == set[x] )
		return x;

	return set[x] = find( set[x] );
}

int
main() {

	int		nm;
	int		nq;
	int		i;
	int		u, v;
	int		su, sv;

	while ( ~scanf("%d", &nm) ) {
	
		for ( i = 1; i <= nm; i++ )
			set[i] = i;
		memset( nod + 1, 0, nm * sizeof(Node));

		for ( i = 1; i <= nm; i++ )	{

			scanf("%d", &nod[i].key);
			mok[i] = nod + i;
		}
		scanf("%d", &nq);
		while ( nq-- ) {
		
			scanf("%d%d", &u, &v);
			
			su = find(u);
			sv = find(v);

			if ( su == sv ) {
			
				puts("-1");
				continue;
			}

			mok[su] = Adjst( mok[su] );
			mok[sv] = Adjst( mok[sv] );

			mok[sv] = Merge( mok[su], mok[sv] );
			set[su] = sv;

			printf("%d\n", mok[sv]->key);
		}
	}

	return 0;
}

单词解释:

aggressive:adj, 侵略性的,好斗的

quarrel:vt, 吵架,争论

duel:vi/n, 决斗

conflict:vt/n, 冲突,争斗

reduce:vt, 减少

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值