题意:一开始有N只猴子,,每只都有一个力量值.,并且互不认识,后来 它们之间发生了M次斗争。 每次两次两只猴子a,b斗争是, a和 b都会从他们自己的朋友圈里拉出一个最强的朋友, 之后最强的这两只猴子打, 打完后两只猴子的力量值分别减半.。并且 两只猴子的朋友圈的所有人都互相认识(也就是以后不会再打了)。问题是对于每次斗争, 若a,b是朋友, 那么输出-1, 否则输出斗争后它们的朋友圈里最强猴子的力量值。
分析:要表示集合的合并查找操作就是并查集最好了;要维护每次的最大值,就可以使用大顶堆,但还要方便的进行堆之间的合并操作,采用左偏树是最好的。该題也相当于左偏树的练习题了。
#include<iostream>
using namespace std;
#define N 100005
class CLeftistTree
{
private:
struct Set //并查集
{
int m_iParent; //父节点
int m_iTreeRoot; //指向左偏树的根节点
};
struct Node //左偏树节点
{
int m_iLeft; //左子树
int m_iRight; //右子树
int m_iValue; //键值
int m_iDis; //距离
};
public:
CLeftistTree(){}
~CLeftistTree(){}
void ReadValue(int n);
int Process(int a,int b);
private:
int FindRoot(int x);
void UnionSet(int a,int b,int p);
int MergeTree(int a,int b);
Set m_sSet[N];
Node m_sNode[N];
};
int CLeftistTree::MergeTree(int a,int b)
{
if(a==0) return b;
if(b==0) return a;
if(m_sNode[a].m_iValue<m_sNode[b].m_iValue)
swap(a,b); //值大的作为根节点(相当于大顶堆)
m_sNode[a].m_iRight=MergeTree(m_sNode[a].m_iRight,b); //合并到右子树
if(m_sNode[m_sNode[a].m_iLeft].m_iDis<m_sNode[m_sNode[a].m_iRight].m_iDis) //确保是一颗左偏树
swap(m_sNode[a].m_iLeft,m_sNode[a].m_iRight);
if(m_sNode[a].m_iRight==0) m_sNode[a].m_iDis=0; //更新距离
else
m_sNode[a].m_iDis=m_sNode[m_sNode[a].m_iRight].m_iDis+1;
return a;
}
void CLeftistTree::UnionSet(int a,int b,int p)
{
if(a<b)
{
m_sSet[a].m_iParent=b;
m_sSet[b].m_iTreeRoot=p;
}
else
{
m_sSet[b].m_iParent=a;
m_sSet[a].m_iTreeRoot=p;
}
}
int CLeftistTree::FindRoot(int x)
{
int root=x;
int parent;
while(root!=m_sSet[root].m_iParent) root=m_sSet[root].m_iParent;
parent=m_sSet[x].m_iParent;
while(parent!=root) //路径压缩
{
m_sSet[x].m_iParent=root;
x=parent;
parent=m_sSet[x].m_iParent;
}
return root;
}
int CLeftistTree::Process(int a,int b)
{
int x,y,p,q;
if(FindRoot(a)==FindRoot(b)) //在同一个集合中
return -1;
x=m_sSet[FindRoot(a)].m_iTreeRoot; //找到左偏树的根
y=m_sSet[FindRoot(b)].m_iTreeRoot;
p=MergeTree(m_sNode[x].m_iLeft,m_sNode[x].m_iRight); //取出根,合并左右子树到左偏树
q=MergeTree(m_sNode[y].m_iLeft,m_sNode[y].m_iRight);
m_sNode[x].m_iValue/=2;
m_sNode[x].m_iDis=m_sNode[x].m_iLeft=m_sNode[x].m_iRight=0; //两个最大猴子的值减半
m_sNode[y].m_iValue/=2;
m_sNode[y].m_iDis=m_sNode[y].m_iLeft=m_sNode[y].m_iRight=0;
p=MergeTree(p,x); //分别插入原来的树中
q=MergeTree(q,y);
p=MergeTree(p,q); //得到一棵完整的树
UnionSet(FindRoot(a),FindRoot(b),p); //并查集合并
return m_sNode[p].m_iValue;
}
void CLeftistTree::ReadValue(int n)
{
int i;
for(i=1;i<=n;i++)
{
scanf("%d",&m_sNode[i].m_iValue);
m_sNode[i].m_iDis=m_sNode[i].m_iLeft=m_sNode[i].m_iRight=0;
m_sSet[i].m_iParent=i;
m_sSet[i].m_iTreeRoot=i;
}
}
CLeftistTree left_tree;
int main()
{
int n,M,x,y;
while(scanf("%d",&n)==1)
{
left_tree.ReadValue(n);
scanf("%d",&M);
while(M--)
{
scanf("%d %d",&x,&y);
printf("%d\n",left_tree.Process(x,y));
}
}
return 0;
}HDU ACM 1512 Monkey King->左偏树+并查集
原文地址:http://blog.csdn.net/a809146548/article/details/45440781