信息奥赛一本通1427:数列极差
作者:互联网
题目链接:http://ybt.ssoier.cn:8088/problem_show.php?pid=1427
1427:数列极差
时间限制: 1000 ms 内存限制: 65536 KB
提交数: 1681 通过数: 757
【题目描述】
在黑板上写了N个正整数作成的一个数列,进行如下操作:每一次擦去其中的两个数a和b,然后在数列中加入一个数a×b+1,如此下去直至黑板上剩下一个数,在所有按这种操作方式最后得到的数中,最大的max,最小的为min,则该数列的极差定义为M=max−min。
【输入】
第一行,一个数为N;
第二行,N个数。
【输出】
输出极差。
【输入样例】
3 1 2 3
【输出样例】
2
【来源】
这个题是一道贪心题。
先建一个堆,求最大值的过程就是从堆中选出两个最小的数,再将这两个数相乘+1加入堆,
求最小值的过程就是从堆中选出两个最大的数,再将这两个数相乘+1加入堆。
以求最大值为例:
一个比较通俗的证明
先选两个小的数,因为要+1,让这个1与更大数相乘能让它更大,所以每次合并都尽量和最小的。
一个较清晰的证明
假设有3个数a,b,c(a<=b<=c)则三种合并顺序abc,acb,bca,所得值分别为(a*b+1)*c+1=a*b*c+c+1,(a*c+1)*b+1=a*b*c+b+1,(b*c+1)*a+1=a*b*c+a+1,
显然a*b*c+c+1最大,更多的数也是如此,所以每次合并都尽量和最小的。
求最小值就是把求最大值的过程反过来。
题目中要求分别建大根堆和小根堆,可以用函数指针,让它分别等于a>b函数指针和a<b函数指针,这样就不用把堆的代码打两遍(其实复制粘贴在改几个地方也可以)。
代码:
#include<stdio.h> long long a[100001],b[100001],maxx,minn,a1,a2; int lena,n; int (*cmp)(long long,long long);//比较的函数指针 int dgd(long long aaa,long long bbb){//大根堆比较 return aaa>bbb; } int xgd(long long aaa,long long bbb){//小根堆比较 return aaa<bbb; } long long geta(){//从堆中弹出一个元素 int now,next; long long res,p; res=a[1]; a[1]=a[lena--]; now=1; while((now<<1)<=lena){ next=now<<1; if(next<lena&&(*cmp)(a[next+1],a[next]))next++; if((*cmp)(a[now],a[next]))break; p=a[now];a[now]=a[next];a[next]=p; now=next; } return res; } void puta(long long res){//从堆中加入一个元素 int now,next; long long p; a[++lena]=res; now=lena; while(now>1){ next=now>>1; if((*cmp)(a[next],a[now]))break; p=a[now];a[now]=a[next];a[next]=p; now=next; } return; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%lld",b+i); cmp=xgd;//计算最大值 for(int i=1;i<=n;i++)puta(b[i]); while(lena>1){ a1=geta(); a2=geta(); puta(a1*a2+1); } maxx=geta(); cmp=dgd;//计算最小值 for(int i=1;i<=n;i++)puta(b[i]); while(lena>1){ a1=geta(); a2=geta(); puta(a1*a2+1); } minn=geta(); printf("%lld",(maxx-minn)); return 0; }
我可能写的不好,如果有问题,请帮忙指出,谢谢。
标签:1427,int,geta,long,极差,next,a2,奥赛,now 来源: https://www.cnblogs.com/sy666/p/12815247.html