题目描述
有两个长度都为N的序列A和B,在A和B中各取一个数相加可以得到N2个和,求这N2个和中最小的N个。
输入
第一行一个正整数N(1 <= N <= 100000)。
第二行N个整数Ai,满足Ai <= Ai+1且Ai <= 109
第三行N个整数Bi,满足Bi <= Bi+1且Bi <= 109
输出
输出仅有一行,包含N个整数,从小到大输出这N个最小的和,相邻数字之间用空格隔开。
样例输入 Copy
3
2 6 6
1 4 8
样例输出 Copy
3 6 7
提示
建议用最小堆实现。
由于题目的数据量很大,最后要求输出的是n个最小的和,因此堆的规模可以为n,即建立一个规模为n的堆并维护它。题目中有一个条件特别重要,“第二行N个整数Ai,满足Ai <= Ai+1且Ai <= 109,第三行N个整数Bi,满足Bi <= Bi+1且Bi <= 109”,题目中说“Ai <= Ai+1,Bi <= Bi+1”,也就是说,A和B两个序列是递增的。因此在做加法找最小值的时候,有的情况可以直接跳过。
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
int heap[100002];
int a[100002],b[100002];
int n;
void downAdjust(int s,int m)//向下调整
{
int j;
int rc=heap[s];
for(j=2*s; j<=m; j=j*2)
{
if(j<m&&heap[j]<heap[j+1])
j++;
if(rc>=heap[j])break;
heap[s]=heap[j];
s=j;
}
heap[s]=rc;
}
void creatHeap()//建堆
{
for(int i=n/2; i>=1; i--)
downAdjust(i,n);
}
void heapSort()//堆排序
{
for(int i=n; i>1; i--)
{
swap(heap[1],heap[i]);
downAdjust(1,i-1);
}
}
int main()
{
int i,j,x;
scanf("%d",&n);
for(i=1; i<=n; i++)
scanf("%d",&a[i]);
for(i=1; i<=n; i++)
scanf("%d",&b[i]);
for(i=1; i<=n; i++)//将a[1]与数组b的每个元素依次相加,初始建立一个规模为n的大顶堆
heap[i]=a[1]+b[i];
creatHeap();
for(i=2; i<=n; i++)
{
for(j=1; j<=n; j++)
{
x=a[i]+b[j];//依次将其余的元素相加
if(x<heap[1])//如果相加的和小于堆顶(堆顶是堆内最大的元素),
//就将这个元素纳入堆并进行向下调整,是堆重新成为大顶堆,
//每次都找到比堆顶元素小的和并替换堆顶(堆内最大的元素),最终找n个最小的元素。
{
heap[1]=x;
downAdjust(1,n);
}
else break;//这里特别重要!!!A、B序列是递增的,因此如果出现了a[i]+b[j]>heap[1],
//再往后加的话和只能越来越大,不可能出现小于heap[1]的,因此要及时跳出循环。
}
}
heapSort();
for(i=1; i<n; i++)printf("%d ",heap[i]);
printf("%d\n",heap[n]);
return 0;
}