题目链接:点击打开链接
题意:一群猴子,每个猴子均在唯一的一个组里,可单独成组。当两个猴子要打架时,若在同一组,不可以打架,若不在一组,每个猴子找到本组中最强壮的去迎战,打完架的两个猴子,强壮值减半,而且这两组猴子合并为一组。
由最强壮的猴子迎战,而且要合并组,可以想到左偏树(有合并操作的堆)。不同于简单的左偏树模板的是,由于要尽快的找到某个节点的堆顶,借助于并查集的思路,存每个节点的父亲节点,而且还要写下滤函数,当猴子的强壮值改变,进行下滤。
代码:
// HDU 1512 Monkey King.cpp 1388ms
#include "stdafx.h"
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define MAXN 100005
int value[MAXN], lef[MAXN], rig[MAXN], fa[MAXN], dist[MAXN];
int n;
void init() {
for (int i = 1; i <= n; i++) {
scanf("%d", &value[i]);
lef[i] = rig[i] = -1;
dist[i] = 0;
fa[i] = i;
}
}
int getFather(int x) {//并查集思想
return fa[x] = (fa[x] == x ? x : getFather(fa[x]));
}
int merge(int x, int y) {
if (x == -1) {
return y;
}
if (y == -1) {
return x;
}
int a = (value[x] > value[y] ? x : y);
int b = (a == x ? y : x);
rig[a] = merge(rig[a], b);
if (lef[a] == -1 || dist[lef[a]] < dist[rig[a]]) {
swap(lef[a], rig[a]);
}
if (lef[a] != -1) {
fa[lef[a]] = a;
}
if (rig[a] != -1) {
fa[rig[a]] = a;
}
dist[a] = dist[rig[a]] + 1;
return a;
}
void down(int x) {//下滤
int i, child;
int te = value[x];
for (i = x; lef[i] != -1; i = child) {
child = lef[i];
if (rig[i] != -1 && value[rig[i]] > value[lef[i]]) {
child = rig[i];
}
if (te > value[child]) {
break;
}
value[i] = value[child];
}
value[i] = te;
}
void decrease(int x) {
value[x] /= 2;
down(x);
}
int main(){
int m,x,y;
while (scanf("%d", &n) != EOF) {
init();
scanf("%d", &m);
for (int i = 0; i < m; i++) {
scanf("%d%d", &x, &y);
int a = getFather(x);
int b = getFather(y);
if (a == b) {
printf("-1\n");
}
else {
decrease(a);
decrease(b);
int re = merge(a, b);
printf("%d\n", value[re]);
}
}
}
return 0;
}
这个写法更快,不过,虽然基于类封装实现更能体现面向对象的思想,但这种思想更适合用于工程开发,做算法题注重思维,如何使用更少的时间和空间去解决问题才是关键,有的题目面向对象的做法开销会比较大,不一定是最佳方案。
// HDU 1512 Monkey King.cpp 1123ms
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define MAXN 100005
class LeftistTree {
private:
int n;
int value[MAXN], left[MAXN], right[MAXN], fa[MAXN], dist[MAXN];
public:
void init(int t) {
n = t;
for (int i = 1; i <= n; i++) {
scanf("%d", &value[i]);
left[i] = right[i] = -1;
dist[i] = 0;
fa[i] = i;
}
}
int getFather(int x) {
return fa[x] = (fa[x] == x ? x : getFather(fa[x]));
}
int merge(int x, int y) {
if (x == -1) {
return y;
}
if (y == -1) {
return x;
}
int a = (value[x] > value[y] ? x : y);
int b = (a == x ? y : x);
right[a] = merge(right[a], b);
fa[right[a]] = a;
if (left[a] == -1 || dist[left[a]] < dist[right[a]]) {
swap(left[a], right[a]);
}
if(left[a] != -1){
fa[left[a]] = a;
}
if(right[a] != -1){
fa[right[a]] = a;
}
dist[a] = dist[right[a]] + 1;
return a;
}
void decrease(int x) {
value[x] /= 2;
down(x);
}
void down(int x) {
int i,child;
int te = value[x];
for (i = x; left[i] != -1; i = child) {
child = left[i];
if(right[i] != -1 && value[right[i]] > value[left[i]]){
child = right[i];
}
if (te > value[child]) {
break;
}
value[i] = value[child];
}
value[i] = te;
}
int getValue(int x) {
return value[x];
}
};
int main(){
int n,m,x,y;
LeftistTree *tree = new LeftistTree();
while (scanf("%d", &n) != EOF) {
tree->init(n);
scanf("%d", &m);
for (int i = 0; i < m; i++) {
scanf("%d%d", &x, &y);
int a = tree->getFather(x);
int b = tree->getFather(y);
if (a == b) {
printf("-1\n");
}
else {
tree->decrease(a);
tree->decrease(b);
int re = tree->merge(a, b);
printf("%d\n", tree->getValue(re));
}
}
}
return 0;
}