问题 B: 序列合并

题目描述
有两个长度都为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;
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值