平衡树的基本操作

【题目名称】:

2056 【2002湖南】营业额统计

 

【题目简述】:

Description

  Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况。
  Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营业额。分析营业情况是一项相当复杂的工作。由于节假日,大减价或者是其他情况 的时候,营业额会出现一定的波动,当然一定的波动是能够接受的,但是在某些时候营业额突变得很高或是很低,这就证明公司此时的经营状况出现了问题。经济管 理学上定义了一种最小波动值来衡量这种情况:
  该天的最小波动值=min { | 该天以前的某天的营业额-该天的营业额 | }
  当最小波动值越大时,就说明营业情况越不稳定。
  而分析整个公司的从成立到现在营业情况是否稳定,只需要把每一天的最小波动值加起来就可以了。你的任务就是编写一个程序帮助Tiger来计算这一个值。
  第一天的最小波动值为第一天的营业额。

Input

  第一行为正整数n(n<=32767) ,表示该公司从成立一直到现在的天数,接下来的n行每行有一个正整数a(<=1000000) ,表示第i天公司的营业额。

Output

  仅有一个正整数,即每一天的最小波动值的总和。结果小于2^31

Sample Input

  6
  5
  1
  2
  5
  4
  6

Sample Output

  12

Hint

结果说明:5+|1-5|+|2-1|+|5-5|+|4-5|+|6-5|=5+4+1+0+1+1=12

 

【题目考点】:

 

    平衡树的基本操作。包括有:1.平衡树的插入;2.平衡树中已知元素,求它是第几小;3.已知第几小,求该元素

【题目分析】:

    

该题目是一个所描述的问题是一个经典的关于平衡树的问题。对于输入的每一天的营业额,必须在其前面已输入的营业额中,找到与其最接近的营业额,即对于当前元素ki,必须在其前面找到一个元素kj1=<j<i),使的|ki-kj|最小,最后求出所有的ki的值之和即为Answer

那么,可以在输入的同时,进行元素的插入以及元素的查找,来寻找min|ki-kj|)。为了节省空间,本题可以用  1.指针 2.数组模拟邻接表 来做,具体方法如下:

 

【参考代码】:

 

#include<iostream>

using namespace std;

int n,ans;//n表示一共的天数

struct node

{int w,pro,ln,rn;node *lc,*rc;}*p;

/*指针的建立。w是该位置的值,pro为一个随机的int数,用于维护堆的性质。ln,rn分别表示左孩子和右孩子的个数(都包括自身),lc,rc分别指向左右孩子*/

void rturn(node *&a) //右旋

{

     node *b;

     b=a->lc;

     a->lc=b->rc;

     a->ln=b->rn;

     b->rc=a;

     b->rn=a->ln+a->rn;

     a=b;

}

void lturn(node *&a)//左旋

{

     node *b;

     b=a->rc;

     a->rc=b->lc;

     a->rn=b->ln;

     b->lc=a;

     b->ln=a->ln+a->rn;

     a=b;

}

/* 左旋和右旋,是平衡树和排序二叉树的区别,用于维护堆的性质,来尽量提高时间效率。其操作实现是:将不满足堆的性质的两个元素进行交换,保证儿子的pro比父亲的要大。*/

void charu(node *&p,int y)//插入操作

{

     if(p==NULL)//p为空时,还没有元素,就将它作为根结点插入

       {

         p=new node;p->w=y;p->pro=rand();

         p->ln=1;p->rn=1;p->lc=NULL;p->rc=NULL;

         return ;

       }

     if(y<p->w)  //as the left child

       { p->ln++;//左儿子个数要++

         charu(p->lc,y);

         if(p->lc->pro<p->pro) rturn(p);//维护堆的定律

       }

       else  //as the right child

         { p->rn++;//右儿子个数++

           charu(p->rc,y);

           if(p->rc->pro<p->pro) lturn(p);//维护堆的定律   

         }     

}

int chazhao(node *p,int y)//第一种查找操作,返回y是第几小的

{

     int k,j;

     j=0;

     while(p!=NULL)

       {

            k=p->ln+j;

            if(y==p->w) break;

            if(y<p->w) p=p->lc;

              else {j=k;p=p->rc;}

       }

     if(p==NULL) return 0;

       else return k;

}

int chazhao2(node *p,int x)//第二种查找操作,返回第x小的元素是哪个

{

    int k,j;

    j=0;

    while(p!=NULL)

      {

           k=p->ln+j;

           if(x==k) break;

           if(x<k) p=p->lc;

             else {j=k;p=p->rc;}

      }

    return p->w;

}

int main()

{

    int i,x,k;

    ans=0;//赋初值为0

    cin>>n;

    for(i=1;i<=n;i++) //循环输入,边输入边操作

      {

           scanf("%d",&x);//输入每天的营业额

           charu(p,x);//进行插入

           if(i==1) ans+=x; /*剪枝操作*/

             else

              {

                   k=chazhao(p,x);//

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值