K路归并问题
k路归并:即给定N个有序的序列,要求把这N个有序的序列合并成一个有序的序列。
【问题描述】
有两个长度为 N 的序列 A 和 B,在 A 和 B 中各任取一个数橡胶可以得到
N2 个和,求这N2 个和中最小的 N个。
【文件输入】
第一行输入一个正整数N;第二行N个整数Ai 且Ai≤109;第三行N个整数Bi,
且Bi≤109
。
【文件输出】
输出仅一行,包含 n 个整数,从小到大输出这 N个最小的和,相邻数字之间用
空格隔开。
【样例输入】
5 1 3 2 4 5
6 3 4 1 7
【样例输出】
2 3 4 4 5
【数据规模】
对于 50%的数据,满足 1≤N≤1000;
对于 100%的数据,满足 1≤N≤100000。
分析:
• 数据很大,但是只要求求出前N个,所以可以想到用堆优化
每个表的元素都是从左到右移入新表
• 把每个表的当前元素放入二叉堆中, 每次删除最小值并放入新表中, 然后加入此序列的下一个元素
• 每次操作需要logk时间, 因此总共需要nlogk的时间
• 可以把这些和看成n个有序表:
– A[1]+B[1] <= A[1]+B[2] <= A[1]+B[3] <=…
– A[2]+B[1] <= A[2]+B[2] <= A[2]+B[3] <=…
–…
– A[n]+B[1] <= A[n]+B[2] <= A[n]+B[3] <=…
综上所述,可以采用K路归并:
下面给出代码:
#include <fstream> #include <cstring> using namespace std; ifstream cin("sequence.in"); ofstream cout("sequence.out"); struct { int q,num,c; }e[100001]; int a[100001],b[100001]; void heap(int l,int r) { int t; t=l*2; while (t<=r) { if (t<r&&e[t].c>e[t+1].c) t++; if (e[t].c<e[t>>1].c) { swap(e[t].c,e[t>>1].c); swap(e[t].num,e[t>>1].num); swap(e[t].q,e[t>>1].q); t=t*2; }else break; } } void qsort1(int l,int r) { int i=l,j=r,x=a[(l+r)>>1]; do { while (a[i]<x) i++; while (a[j]>x) j--; if (i<=j) swap(a[i++],a[j--]); }while (i<=j); if (i<r) qsort1(i,r); if (j>l) qsort1(l,j); } void qsort2(int l,int r) { int i=l,j=r,x=b[(l+r)>>1]; do { while (b[i]<x) i++; while (b[j]>x) j--; if (i<=j) swap(b[i++],b[j--]); }while (i<=j); if (i<r) qsort2(i,r); if (j>l) qsort2(l,j); } int main() { int n,m; cin>>n; int i; for (i=1;i<=n;i++) cin>>a[i]; for (i=1;i<=n;i++) cin>>b[i]; qsort1(1,n); qsort2(1,n); for (i=1;i<=n;i++) { e[i].q=i; e[i].num=1; e[i].c=a[i]+b[1]; } for (i=n>>1;i>0;i--) heap(i,n); int tot; tot=1; m=n; while (tot<=m) { cout<<e[1].c<<" "; (e[1].num++); e[1].c=a[e[1].q]+b[e[1].num]; heap(1,n); tot++; } cout<<endl; return 0; }