首页 > 代码库 > [ACM] POJ 2442 Sequence (堆的性质)
[ACM] POJ 2442 Sequence (堆的性质)
Time Limit: 6000MS | Memory Limit: 65536K | |
Total Submissions: 7011 | Accepted: 2262 |
Description
Input
Output
Sample Input
1 2 3 1 2 3 2 2 3
Sample Output
3 3 4
Source
有m行,每行n个数,从每行中取出一个数相加一起求出sum,这样的sum有n的m次方个。要求的前n个最小的sum值。
第一次使用STL里面的堆,一开始对pop_heap()有点不太理解,后来明白了,比如对数组heap[n],最大下标为n-1建堆,默认的是建立大顶堆,heap[0]是数组中最大的数,写一条pop_heap()语句,就是把heap[0]和 heap[n-1]互换,然后重新对 heap[0]到heap[n-2] (除去最后一个元素)重新建堆,heap[0]依然是最大值,这样相当于更新最大值的操作。
对于本题,堆的性质可以很好的运用。 三个数组old[n] , new [n] , heap[n],old[]是用来保存前i行元素每行取出一个元素加起来所得的前n个最小sum,new[n]是第i+1行的输入元素,heap[n]是中间数组,用来建堆,并更新其最大值元素,每处理完第i行,获得前i行的前n个最小sum(这时保存在heap[]里面),就把heap[]元素赋给old[],这样前m行都处理完毕后,再对old[]数组从小到大排一次序,输出即为所求了。
下面具体说一下heap[]的作用。
一开始把第一行的元素赋给old[], 并对其排序,然后将第一行的元素都加上第二行排序后的第一个元素,把n个值放到heap[]里面,这样就获得了一个临时的前两行的前n个sum值,但这并不一定是前两行最小的前n个sum值 ,因为第二行也有n个元素,得枚举出所有情况(即第二行的第二个,第三个...元素分别与第一行的所有值相加获得sum值)要在这些所有的情况里面取最小的前n个,这就要发挥堆的作用了,前面说临时的前n个sum值保存在heap[]里面了,然后对heap[]进行建堆make_heap(heap,heap+n);默认大顶堆,即heap[0]是最大值,在枚举的时候如果有发现求出的一个临时sum值(temp)比heap[ 0 ]要小,那就需要对heap[0]更新,因为我们是要求前n个最小的sum值,用 pop_heap(heap,heap+n);把最大值heap[0]与heap[n-1]互换,然后另heap[n-1] = temp;这样heap[]最大值就更新了(原来的最大值从数组中去除),但不知道是数组中的哪一个值,push_heap(heap,heap+n); 对其重新建堆,这样heap里面的最大的就是heap[0] 了。枚举完,更新完就把前两行的前n个最小的sum值求出来了,剩下的行是相同的操作。 上面的排序是为了减少不必要的操作,在枚举时可以提前退出,这就和 排序好的 2,4,5,6我们要最小的值,那就取2,不用考虑后面的数了一样。
代码:
#include <iostream> #include <algorithm> using namespace std; const int maxn=2010; int Old[maxn],New[maxn],heap[maxn]; int main() { int t;cin>>t; int m,n; while(t--) { cin>>m>>n; for(int i=0;i<n;i++) cin>>Old[i]; sort(Old,Old+n); m--; while(m--) { for(int i=0;i<n;i++) cin>>New[i]; sort(New,New+n); for(int i=0;i<n;i++) heap[i]=Old[i]+New[0]; make_heap(heap,heap+n);//建立大顶堆,heap[0]是最大的值 //通过枚举把heap里面的大值都换掉,每次换的都是当前的最大值 for(int i=1;i<n;i++)//新数据的剩下的值 { bool ok=0; for(int j=0;j<n;j++)//旧数组 { int temp=New[i]+Old[j]; if(temp<heap[0]) { pop_heap(heap,heap+n);//heap数组里面的最后一个元素和第一个元素(最大值)互换,并把除最后一个元素以外的元素重新建堆 heap[n-1]=temp; push_heap(heap,heap+n); ok=1;//可以更新最大值 } else //当旧数组的第j个数和新数组的数第i个数相加已经没有比大值还小的了,就不用再继续加旧数组了,因为旧数组是排好序的,再加也会比heap里面的最大值大 break; } if(!ok)//如果新数据的第i个数分别和旧数组的n个数相加都没有更新heap[]里面的最大值,那么第i+1个数肯定也不会更新,因为排好序 break; } for(int i=0;i<n;i++) Old[i]=heap[i]; sort(Old,Old+n); } for(int i=0;i<n-1;i++) cout<<Old[i]<<" "; cout<<Old[n-1]<<endl; } return 0; }
下面贴一下堆的知识:http://blog.csdn.net/hnust_xiehonghao/article/details/9172875转载
最大堆最小堆代码实现
http://blog.csdn.net/xiaoxiaoxuewen/article/details/7570621
最大堆 最小堆原理图
http://www.cnblogs.com/wu8685/archive/2010/12/30/1922218.html
STL 的堆操作
http://hi.baidu.com/solofancy/item/14acd02b9743f7d30f37f927
http://blog.csdn.net/morewindows/article/details/6967409
STL 的堆操作 来自上面的博客
STL里面的堆操作一般用到的只有4个:make_heap();、pop_heap();、push_heap();、sort_heap();
他们的头文件函数是#include <algorithm>
首先是make_heap();
他的函数原型是:void make_heap(first_pointer,end_pointer,compare_function);
一个参数是数组或向量的头指针,第二个向量是尾指针。第三个参数是比较函数的名字。在缺省的时候,默认是大跟堆。(下面的参数都一样就不解释了)
作用:把这一段的数组或向量做成一个堆的结构。范围是(first,last)
然后是pop_heap();
它的函数原型是:void pop_heap(first_pointer,end_pointer,compare_function);
作用:pop_heap()不是真的把最大(最小)的元素从堆中弹出来。而是重新排序堆。它
把first和last交换,然后将[first,last-1)的数据再做成一个堆。
接着是push_heap() void pushheap(first_pointer,end_pointer,compare_function);
作用:push_heap()假设由[first,last-1)是一个有效的堆,然后,再把堆中的新元素加
进来,做成一个堆。
最后是sort_heap()void sort_heap(first_pointer,end_pointer,compare_function);
作用是sort_heap对[first,last)中的序列进行排序。它假设这个序列是有效堆。(当然
,经过排序之后就不是一个有效堆了)
实例:
#include<algorithm> #include<cstdio> using namespace std; bool cmp(int a,int b) { return a>b; } int main() { int i,number[20]={29,23,20,22,17,15,26,51,19,12,35,40}; make_heap(&number[0],&number[12]); //结果是:51 35 40 23 29 20 26 22 19 12 17 15 for(i=0;i<12;i++) printf("%d ",number[i]); printf("\n"); make_heap(&number[0],&number[12],cmp); //结果:12 17 15 19 23 20 26 51 22 29 35 40 for(i=0;i<12;i++) printf("%d ",number[i]); printf("\n"); //加入元素8 number[12]=8; //加入后调整 push_heap(&number[0],&number[13],cmp); //结果:8 17 12 19 23 15 26 51 22 35 40 20 for(i=0;i<13;i++) printf("%d ",number[i]); printf("\n"); //弹出元素8 pop_heap(&number[0],&number[13],cmp); //结果:12 17 15 19 23 20 26 51 22 29 35 40 for(i=0;i<13;i++) printf("%d ",number[i]); printf("\n"); sort_heap(&number[0],&number[12],cmp); //结果不用说都知道是有序的了! for(i=0;i<12;i++) printf("%d ",number[i]); return 0; }