二叉堆例题解题报告代码--poj3253、poj2442、poj2010、poj3481

来源:互联网 发布:淘宝运营工资大概多少 编辑:程序博客网 时间:2024/05/24 07:36
poj3253 类似于哈夫曼树,每次选择所有数中最小的两个,所以这里要建造小根堆,并删除最小的两个数,然后求这两个数的和之后再插入,这里用二叉堆的 make_heap(开始要建堆)、heap(中间要调整)、push_heap(要把和插入,但是对于这里,可以简单的求和,放入堆顶,然后调整一下就ok)、pop_heap(对于选择的数删除)
#include <stdio.h>#define ll long long#define MAX 20002int n;int a[MAX];void heap(int m){    int t;    while(m*2 <= n)    {        m *= 2;        if(m+1 <= n && a[m]>a[m+1]) m ++;        if(a[m] < a[m/2])        {            t = a[m];            a[m] = a[m/2];            a[m/2] = t;        }        else break;    }}int main(){    scanf("%d",&n);    for(int i = 1; i <= n; i ++) scanf("%d",&a[i]);//建堆    for(int i = n/2; i > 0; i --) heap(i);    ll ans = 0,pre;    while(n > 1)    {         //选择第一个,并且删除        pre = a[1];        a[1] = a[n];        n--;        heap(1);//再选择第一个        pre += a[1];//把和放入堆顶 接着调整        a[1] = pre;        heap(1);        ans += pre;//求答案    }    printf("%I64d\n",ans);    return 0;}


poj2442 题意:有m个序列,每个序列n个数,我们在每个序列中选一个数,那么就有m个数,对应这m个数有一个和,那么对于所有的数,总共有m^n个选法,要求从小到大输出其中最下的n个选法的和
分析:直接暴力时间复杂度太高,这里用到一点dp,对于m个序列,先求出第k-1项序列的n个数,放于A数组,每个数都是k-1个数的和,那么对于第k个序列,把这个序列的第一个和A数组的每一个相加放到B数组,然后把第k个序列的每一个都和A数组的每一个数相加,然后和B中的最大数相比较,如果小,就互换(因为要n个最小的),具体处理如下(用到滚动数组)操作中只要用到一个heap,和最开始的一个make_heap:
//180K 1922MS#include <cstdio>#include <algorithm>using namespace std;#define MAX 2002int n,m;int a[2][MAX]; //滚动数组void heap(int r[],int m){    int t;    while(m*2 <= n)    {        m *= 2;        if(m +1 <= n && r[m] < r[m+1]) m ++;        if(r[m] > r[m/2])        {            t = r[m/2];            r[m/2] = r[m];            r[m] = t;        }        else break;    }}int main(){    int T;    int t,flag;    scanf("%d",&T);    while(T --)    {        flag = 0;        scanf("%d%d",&m,&n);        for(int i = 1; i <= n; i ++) scanf("%d",&a[0][i]);        for(int i = n/2; i > 0; i --) heap(a[flag],i); //首先我们把第一个序列建堆        for(int i = 2; i <= m; i ++,flag = 1-flag)        {            scanf("%d",&t);            for(int j = 1; j <= n; j ++) a[1-flag][j] = a[flag][j] + t;//把下一个序列的第一个与上一个序列每一个相加得一个新的序列,也具有堆性质            for(int j = 1; j < n; j ++)//对于这个序列的剩下 n-1个数 与 A 中每一个相加再与 B 的最大比较            {                scanf("%d",&t);                for(int k = 1; k <= n; k ++)                {                    if(a[flag][k] + t < a[1-flag][1])                    {                        a[1-flag][1] = a[flag][k] + t;                        heap(a[1-flag],1);          //这里就用到堆的性质                    }                }            }        }        sort(a[flag]+1,a[flag] + n+1); //排序输出        for(int i = 1; i <= n; i ++)        {            printf(i==1 ? "%d":" %d",a[flag][i]);        }        printf("\n");    }    return 0;}


poj2010  题意:这里有C头牛,每头牛有一个考试分数,和需要资助的钱,我们选择其中的N头牛资助,并且我们只有F的钱,而且我们有一个要求,就是选择的这N头牛,我们需要他们的分数的中位数 最大
处理:既然要分数的中位数,我们就先按照sorce从小到大排序,由于要N个,那么选择第k个牛,前面就有N/2个之后也有N/2个,那么我们只要枚举第N/2~C-N/2的牛即可,又因为要中位数最大,我们就从右到左一一枚举,如果当前这头牛满足条件,那么这头牛的分数就是答案
那么对于满足要求这个条件,我们分别为1~N/2和C-N/2~C 维护left 和right 两个最大堆,left_sum 和 right_sum分别记录左右堆的 和,由于枚举过程中从右到左,那么对于左边的 left需要预处理,left[1]~left[N/2]为维护的对,left[N/2+1]~left[C-N/2] 其中的每个left[i] 表示 枚举i 这头牛的时候,左边选择的N/2头牛的最小资助金和
对于每个点i,left[i]+right_sum+cow[i].fa <= F,就是我们的满足要求
(具体操作如下)
#include <cstdio>#include <algorithm>using namespace std;#define MAX 100005int N,C,F;struct Cow{    int s,fa;    bool operator <(const Cow &c)const    {        return s < c.s;    }} cow[MAX];int right[MAX],left[MAX],left_sum,right_sum;void heap(int a[],int n,int m) //最小堆{    int t;    while(m*2 <= n)    {        m *= 2;        if(m+1 <= n && a[m] < a[m+1]) m ++;        if(a[m] > a[m/2])        {            t = a[m/2];            a[m/2] = a[m];            a[m] = t;        }        else break;    }}void make_heap(int a[],int n){    for(int i = n/2; i > 0; i --) heap(a,n,i);}int main(){    while(~scanf("%d%d%d",&N,&C,&F))    {        for(int i = 1; i <= C; i ++) scanf("%d%d",&cow[i].s,&cow[i].fa);//按分数排序        sort(cow+1,cow+C+1);//先构造左边和右边的 N/2 个数// 这里注意i 和 j 表示是左右边的牛, 但是我们对于这N/2个数存放left、right都是从下标为1开始,才能维护堆left_sum = right_sum = 0;        for(int i = 1,j = C-N/2+1; i <= N/2; i ++,j ++)        {            left[i] = cow[i].fa; left_sum += left[i];            right[i] = cow[j].fa; right_sum += right[i];        }        make_heap(left,N/2); //先把左边建堆//因为一开始我们是从C-N/2 这个位置开始往左边枚举,那么对于这个之前我们要预处理一下// left[i] 表示枚举i这头牛时,之前的最小的 N/2 个数之和        for(int i = N/2+1; i <= C-N/2; i ++)        {            left[i] = left_sum; //首先这个left值就是 前面的left_sum//如果这头牛比之前建堆的 N/2头牛的最大要小,就更新堆,为下一头牛的left_sum准备            if(cow[i].fa < left[1])            {//              把最大牛减掉   加上新加入的牛left_sum = left_sum - left[1] + cow[i].fa;                left[1] = cow[i].fa;//更新 调整                heap(left,N/2,1);            }        }        make_heap(right,N/2);        int ans = -1;        for(int i = C-N/2; i > N/2; i --) //开始枚举        {            if(right_sum+left[i]+cow[i].fa <= F) { ans=cow[i].s; break;}//这头牛满足            //如果上面不满足,那么就要往左边移,那么对于当前这头牛就要放到右边的堆当中去            if(cow[i].fa < right[1])            {                right_sum = right_sum-right[1]+cow[i].fa;                right[1]=cow[i].fa;                heap(right,N/2,1);            }        }        printf("%d\n",ans);    }    return 0;}


poj3481 
题意:银行处理系统,每一个顾客有一个标记,还有一个优先级,对于操作1,就是添加一个顾客,操作2,服务优先级高的顾客,操作3 服务优先级低的顾客
分析,分维护一个最大堆,和一个最小堆,处理相对于的服务的时候,比如 操作2,我们在最大堆中删除,并且将次顾客标记处理啦,那么对于后面操作3处理过程中如果也是那个顾客,就可以判断是否标记,标记了就先删除,再处理下一个
//1760K 204MS#include <stdio.h>#include <string.h>#define MAX 1000005struct node{    int k,p;};node h[MAX],l[MAX]; //最大最小堆int n1,n2; //两个堆的人数bool flag[MAX]; //标记void push1(int k,int p)/{    h[++n1].k = k;    h[n1].p = p;    int i = n1;    while(i>1 && h[i/2].p<p)    {        h[i].k = h[i/2].k;        h[i].p = h[i/2].p;        i /= 2;    }    h[i].k = k;    h[i].p = p;}void push2(int k,int p){    l[++n2].k = k;    l[n2].p = p;    int i = n2;    while(i>1 && l[i/2].p>p)    {        l[i].k = l[i/2].k;        l[i].p = l[i/2].p;        i /= 2;    }    l[i].k = k;    l[i].p = p;}void heap1(int m){    int k,p;    while(m*2 <= n1)    {        m*=2;        if(m+1<=n1 && h[m].p<h[m+1].p) m++;        if(h[m].p>h[m/2].p)        {            k = h[m].k;            p = h[m].p;            h[m].k = h[m/2].k;            h[m].p = h[m/2].p;            h[m/2].k = k;            h[m/2].p = p;        }        else break;    }}int get1(){    while(n1>0 && flag[h[1].k])    {        h[1].k = h[n1].k;        h[1].p = h[n1--].p;        heap1(1);    }    if(n1 == 0) return 0;    else    {        flag[h[1].k] = 1;        return h[1].k;    }}void heap2(int m){    int k,p;    while(m*2 <= n2)    {        m*=2;        if(m+1<=n2 && l[m].p>l[m+1].p) m++;        if(l[m].p<l[m/2].p)        {            k = l[m].k;            p = l[m].p;            l[m].k = l[m/2].k;            l[m].p = l[m/2].p;            l[m/2].k = k;            l[m/2].p = p;        }        else break;    }}int get2(){    while(n2>0 && flag[l[1].k])    {        l[1].k = l[n2].k;        l[1].p = l[n2--].p;        heap2(1);    }    if(n2 == 0) return 0;    else    {        flag[l[1].k] = 1;        return l[1].k;    }}int main(){    memset(flag,false,sizeof(flag));    int a,k,p;    n1=n2=0;    while(~scanf("%d",&a))    {        if(!a) continue;        if(a == 1)        {            scanf("%d%d",&k,&p);            flag[k] = 0;            push1(k,p);push2(k,p);        }        else if(a == 2)        {            printf("%d\n",get1());        }        else        {            printf("%d\n",get2());        }    }    return 0;}
个人愚昧观点,欢迎指正与讨论

2 0
原创粉丝点击