hdu 1055 poj 2054 映射二叉堆

本文介绍了一种使用并查集与最大堆解决特定问题的算法。通过维护节点权值,利用最大堆快速找到最大权值节点,并通过并查集高效更新节点间的关系。最终实现了从O(N^2)到O(NlogN)的时间复杂度优化。

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

算法:

算法证明:http://hi.baidu.com/cheezer94/item/7b4a15214b2050022b0f1c0a

代码来源:http://www.cppblog.com/notonlysuccess/archive/2009/07/02/88793.html

ò  1.标记根节点为第一个访问的节点。

ò  2.求出当前未访问节点中的最大点权节点i。

ò  3.将i和它的父亲j合并为一个节点,节点权值为两者权值的算术平均数。在序列P中将j的后继置为i。同时更新树的信息。

ò  4.若当前树中节点数大于1,则转第2步。

ò  5.树的大小为1时算法结束。

ò  6.扫描求得的P序列得到答案。

ò  时间复杂度:O(N^2)。期望得分50-70分。

#include "stdio.h"
#include "string"
#define maxn 1001
struct H{
    int val;
    int cost;
    int time;
    void clear() {
        val = cost = time = 0;
    }
}hh[maxn];
int father[maxn];
int main() {
    int n,r,i;
    while(scanf("%d%d",&n,&r),n+r) {
        for(i =1 ; i <= n ; i ++) {
            scanf("%d",&hh[i].cost);
            hh[i].val = hh[i].cost;
            hh[i].time = 1;
        }
        for(i = 1; i < n ; i ++) {
            int a,b;
            scanf("%d%d",&a,&b);
            father[b] = a;
        }
        while(true) {
            int idx = 0;
            for(i = 1 ; i <= n ; i ++) {
                if(i != r && hh[i].time && (idx == 0 || hh[idx].val * hh[i].time < hh[i].val * hh[idx].time)) {
                    idx = i;
                }
            }
            if(idx == 0)    break;
            int f = father[idx];
            hh[f].cost += hh[idx].cost + hh[idx].val * hh[f].time;
            hh[f].val += hh[idx].val;
            hh[f].time += hh[idx].time;
            hh[idx].clear();
        }
        printf("%d\n",hh[r].cost);
    }
    return 0;
}

 (使用映射二叉堆)

ò  注意到我们每次操作需要得到当前最大权对应的节点i并将i的儿子的父亲改为i的父亲。

ò  O(N)的扫描成为算法复杂度的瓶颈。

ò  如何高效求最大值?

ò  推荐数据结构:最大堆(O(LogN))。

ò  如何高效地将i的儿子的父亲改为i的父亲?

ò  推荐数据结构:并查集(O(α(N)))。

ò  总的时间复杂度:O(NLogN)。期望得分100分。

 

注意根不能放到堆中!

#include "stdio.h"
#include "string"
#define maxn 1001
//-----------------------Binary Heap------------------------------------
struct Heap {
    int val,cost,time,idx;
}hh[maxn];
int pos[maxn];
int len;
bool Prior(Heap a,Heap b) {
    return a.val * b.time > b.val * a.time;
}
void Push(Heap s) {
    int i;
    for(i = ++len ; i > 1 && Prior(s,hh[i/2]); i /= 2) {
        hh[i] = hh[i/2];
        pos[hh[i].idx] = i;
    }
    hh[i] = s;
    pos[hh[i].idx] = i;
}
Heap Pop(int idx) {
    if(idx == -1)    return hh[0];
    Heap ret = hh[idx];
    Heap last = hh[len--];
    int i,s;
    for(i = idx ; i * 2 <= len; i = s) {
        s = i * 2;
        if(s + 1 <= len && Prior(hh[s+1],hh[s])) {
            s ++;
        }
        if(Prior(hh[s],last)) {
            hh[i] = hh[s];
            pos[hh[i].idx] = i;
        } else {
            break;
        }
    }
    hh[i] = last;
    pos[hh[i].idx] = i;
    for(i = idx ; i > 1 && Prior(hh[i],hh[i/2]); i /= 2) {
        Heap buf = hh[i];
        hh[i] = hh[i/2];
        hh[i/2] = buf;
        pos[hh[i].idx] = i;
        pos[hh[i/2].idx] = i/2;
    }
    return ret;
}
//---------------------------------------------------------------
int father[maxn];
int main() {
    int n,r,i;
    hh[0].cost = hh[0].time = hh[0].val = 0;
    while(scanf("%d%d",&n,&r),n+r) {
        len = 0;
        Heap root;
        for(i =1 ; i <= n ; i ++) {
            Heap buf;
            scanf("%d",&buf.cost);
            buf.val = buf.cost;
            buf.time = 1;
            buf.idx = i;
            if(i == r) {
                root = buf;
            } else {
                Push(buf);
            }
        }
        for(i = 1 ; i < n ; i ++) {
            int a,b;
            scanf("%d%d",&a,&b);
            father[b] = a;
        }
        while(len) {
            Heap max = Pop(1);
            int f = father[max.idx];
            if(f == r) {
                root.cost += max.cost + max.val * root.time;
                root.time += max.time;
                root.val += max.val;            
            } else {
                Heap fa = Pop(pos[f]);
                fa.cost += max.cost + max.val * fa.time;
                fa.time += max.time;
                fa.val += max.val;
                fa.idx = f;
                Push(fa);
            }
            pos[max.idx] = -1;
        }
        printf("%d\n",root.cost);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值