一道好题,利用堆求前N小和
题目给出m个长度为n数组,每个数组抽一个数字的和的前n小
算法描述:
sort(all data)
build_heap for data[0][0]+data[1][0], data[0][0]+data[1][1]......
for(i from 1 to end)
for(j from 0 to end)
if(data[0][i]+data[1][j]<heap_top):
heap_pop
heap_push(data[0][i]+data[1][j])
else :
break
sort heap
copy heap to data[0]
简单证明
当k=1时 堆为第一组数据排序,符合条件
设k=b时 符合条件,此时堆保存着前b组数据取和的n小值
当k=b+1时,前b+1组数据和的n小值必然等于第b+1组数据与堆之间和的n小值,因为如果有另一个组合+第b+1组某个数更小的话他必然出现在前b组数据和n小值之中
所以对所有正整数都满足
时间复杂度
首先先对读入的数列从小到大排序花费时间O(m*nlogn)
更新堆用了双重循环,所以更新heapO(n*nlogn)不过好像可以证明更新其实o(nlogn)不然排序就没意义了
sort heap nlgn
复制n
所以算法为O((m+n)*nlogn)
贴个代码,写的搓,手写的堆
#include <stdio.h> #include <stdlib.h> int data[101][2001]; void heap(int now,int n,int data[]) { int x=now,x1=now*2+1,x2=now*2+2; if(x1<n && data[x1]>data[x]) { x=x1; } if(x2<n && data[x2]>data[x]) { x=x2; } if(x!=now) { data[x]^=data[now]; data[now]^=data[x]; data[x]^=data[now]; heap(x,n,data); } } void make_heap(int n,int data[]) { int i=n>>1; for(;i>=0;--i) { heap(i,n,data); } return; } void sort_heap(int n,int data[2001]) { int i=1,x=n-1; for(;i<n;++i) { data[x]^=data[0]; data[0]^=data[x]; data[x]^=data[0]; heap(0,x--,data); } } int cmp(const void *a,const void *b) { return *(int *)a-*(int *)b; } void search(int m, int n) { int tmp[2001],k=1,i=0,j=0; for(;k<m;++k) { for(i=0;i<n;++i) { tmp[i]=data[0][0]+data[k][i]; } make_heap(n,tmp); for(i=1;i<n;++i) { for(j=0;j<n && tmp[0]>data[0][i]+data[k][j];++j) { tmp[0]=data[0][i]+data[k][j]; heap(0,n,tmp); } } sort_heap(n,tmp); for(i=0;i<n;++i) { data[0][i]=tmp[i]; } } for(i=0;i<n;++i) { printf("%d ",data[0][i]); } putchar('\n'); return ; } int main() { int T,m,n,i,j; scanf("%d",&T); while(T--) { scanf("%d %d",&m,&n); for(i=0;i<m;++i) { for(j=0;j<n;++j) { scanf("%d",&data[i][j]); } qsort(data[i],n,sizeof(int),cmp); } search(m,n); } }