【题目名称】: 2056 【2002湖南】营业额统计
|
【题目简述】: Description 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,必须在其前面找到一个元素kj(1=<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);// |