ZOJ 2334 Monkey King
2013-05-16 11:25
381 查看
学了左偏树.
这题用左偏树来合并集合,并查集判是否在同一集合,无敌组合.
以下内容转自: http://www.cppblog.com/guogangj/archive/2009/10/30/99833.html
树这个数据结构内容真的很多,上一节所讲的二叉堆,其实就是一颗二叉树,这次讲的左偏树(又叫“左翼堆”),也是树。
二叉堆是个很不错的数据结构,因为它非常便于理解,而且仅仅用了一个数组,不会造成额外空间的浪费,但它有个缺点,那就是很难合并两个二叉堆,对于“合并”,“拆分”这种操作,我觉得最方面的还是依靠指针,改变一下指针的值就可以实现,要是涉及到元素的移动,那就复杂一些了。
左偏树跟二叉堆比起来,就是一棵真正意义上的树了,具有左右指针,所以空间开销上稍微大一点,但却带来了便于合并的便利。BTW:写了很多很多的程序之后,我发觉“空间换时间”始终是个应该考虑的编程方法。:)
左偏左偏,给人感觉就是左子树的比重比较大了,事实上也差不多,可以这么理解:左边分量重,那一直往右,就一定能最快地找到可以插入元素的节点了。所以可以这样下个定义:左偏树就是对其任意子树而言,往右到插入点的距离(下面简称为“距离”)始终小于等于往左到插入点的距离,当然了,和二叉堆一样,父节点的值要小于左右子节点的值。
如果节点本身不满,可插入,那距离就为0,再把空节点的距离记为-1,这样我们就得出:父节点的距离 = 右子节点距离 + 1,因为右子节点的距离始终是小于等于左子节点距离的。我把距离的值用蓝色字体标在上图中了。
左偏树并一定平衡,甚至它可以很不平衡,因为它其实也不需要平衡,它只需要像二叉堆那样的功能,再加上合并方便,现在来看左偏树的合并算法,如图:
合并操作的代码如下:
?
这题用左偏树来合并集合,并查集判是否在同一集合,无敌组合.
以下内容转自: http://www.cppblog.com/guogangj/archive/2009/10/30/99833.html
树这个数据结构内容真的很多,上一节所讲的二叉堆,其实就是一颗二叉树,这次讲的左偏树(又叫“左翼堆”),也是树。
二叉堆是个很不错的数据结构,因为它非常便于理解,而且仅仅用了一个数组,不会造成额外空间的浪费,但它有个缺点,那就是很难合并两个二叉堆,对于“合并”,“拆分”这种操作,我觉得最方面的还是依靠指针,改变一下指针的值就可以实现,要是涉及到元素的移动,那就复杂一些了。
左偏树跟二叉堆比起来,就是一棵真正意义上的树了,具有左右指针,所以空间开销上稍微大一点,但却带来了便于合并的便利。BTW:写了很多很多的程序之后,我发觉“空间换时间”始终是个应该考虑的编程方法。:)
左偏左偏,给人感觉就是左子树的比重比较大了,事实上也差不多,可以这么理解:左边分量重,那一直往右,就一定能最快地找到可以插入元素的节点了。所以可以这样下个定义:左偏树就是对其任意子树而言,往右到插入点的距离(下面简称为“距离”)始终小于等于往左到插入点的距离,当然了,和二叉堆一样,父节点的值要小于左右子节点的值。
如果节点本身不满,可插入,那距离就为0,再把空节点的距离记为-1,这样我们就得出:父节点的距离 = 右子节点距离 + 1,因为右子节点的距离始终是小于等于左子节点距离的。我把距离的值用蓝色字体标在上图中了。
左偏树并一定平衡,甚至它可以很不平衡,因为它其实也不需要平衡,它只需要像二叉堆那样的功能,再加上合并方便,现在来看左偏树的合并算法,如图:
合并操作的代码如下:
?
#include <iostream> #include <cstdio> using namespace std; const int maxn = 100010; struct node{ int l, r, id, dis, v; }nds[maxn]; int n, m; int merge(int a, int b){ if(a == b) return a; if(a == 0)return b; if(b == 0)return a; if(nds[a].v < nds[b].v)swap(a, b); nds[a].r = merge(nds[a].r, b); nds[nds[a].r].id = a; if(nds[nds[a].l].dis < nds[nds[a].r].dis) swap(nds[a].l,nds[a].r); //右子树的距离如果大于左边则交换左右两树, 使其满足左偏性质 if(nds[a].r == 0)nds[a].dis = 0; else nds[a].dis = nds[nds[a].r].dis + 1; return a; } int find(int p){ return nds[p].id == p ? p : nds[nds[p].id].id = find(nds[p].id); } void readInt(int & t){ char ch; t = 0; ch = getchar(); while(!(ch >= '0' && ch <= '9'))ch = getchar(); while (ch >= '0' && ch <= '9'){ t = t * 10 + ch - '0'; ch = getchar(); } } int delNode(int a){ int l = nds[a].l; int r = nds[a].r; nds[l].id = l; nds[r].id = r; nds[a].l = nds[a].r = nds[a].dis = 0; nds[a].v /= 2; return merge(l, r); } void init(){ for (int i = 1; i <= n; ++i){ nds[i].id = i; nds[i].dis = 0; nds[i].l = 0; nds[i].r = 0; readInt(nds[i].v); } } int main(){ while (scanf("%d", &n) == 1){ init(); scanf("%d", &m); for (int i = 0; i < m; ++i){ int u, v; readInt(u), readInt(v); int pu = find(u), pv = find(v); if(pu == pv){//同一集合 printf("-1\n"); }else{ int tur = delNode(pu);//这个结点 tur = merge(tur, pu);//重新加入树中 int tvr = delNode(pv); tvr = merge(tvr, pv); int ans = nds[merge(tur, tvr)].v;//合并两颗树 printf("%d\n",ans); } } } return 0; }
相关文章推荐
- zoj2334 Monkey King , 并查集,可并堆,左偏树
- ZOJ 2334 Monkey King
- ZOJ 2334 Monkey King(左偏树)
- ZOJ 2334(Monkey King-左偏树第一题)
- zoj 2334 Monkey King
- 【zoj2334】【左偏树】Monkey King
- zoj2334 Monkey King , 并查集,可并堆,左偏树
- ZOJ 2334 HDU 1512 Monkey King
- ZOJ2334 Monkey King 并查集 STL
- ZOJ 2334(Monkey King-左偏树第一题)
- ZOJ2334 Monkey King 并查集 STL
- ZOJ2334 HDU1512 Monkey King,左偏树
- 【左偏树】【ZOJ 2334】Monkey King
- zoj 2334 Monkey King 左偏树+并查集
- ZOJ2334-Monkey King【二项堆】
- zoj 2334 Monkey King(左偏树+并查集)
- ZOJ 2334 Monkey King
- ZOJ 2334 Monkey King(左偏树)
- ZOJ 2334 Monkey King 可并堆左偏树
- zoj 2334 Monkey King