[洛谷P1631]序列合并/[codevs1245]最小的N个和

z

题目链接:http://exam.upc.edu.cn/problem.php?cid=1444&pid=12

题目描述

有两个长度都是N的序列A和B,在A和B中各取一个数相加可以得到N^2个和,求这N^2个和中最小的N个。

输入输出格式

输入格式: 第一行一个正整数N;

第二行N个整数Ai,满足Ai<=Ai+1且Ai<=10^9;

第三行N个整数Bi, 满足Bi<=Bi+1且Bi<=10^9.

【数据规模】

对于50%的数据中,满足1<=N<=1000;

对于100%的数据中,满足1<=N<=100000。

输出格式: 输出仅一行,包含N个整数,从小到大输出这N个最小的和,相邻数字之间用空格隔开。

输入输出样例

输入样例#1: 

2 6 6 
1 4 8 
输出样例#1: 
3 6 7

题意很明确,就是求出合并后的最小值,但要注意的是,两组数据是有重复的数字,但是输出是不需要去重的,有重复的也是可以

思路:用最暴力的方法只能过一半的数据,我用过map去重再排序,但是还是w了,代码也比较繁琐,看了一些博客,知道了原来是可以用堆排序(这里使用最小堆),里面用到了重载,priority_queue

需要预处理一下ab序列,从小到大排一次序,保证后面的操作是最优的,然后就是将a[1]分别于b中的元素进行相加,然后加入到最小堆中,这样堆的首元素就是我们所需要的答案,然后循环n次每次又要进行同样的操作,不过要注意对下一个元素的处理

参考

小堆的耗时少nlogn的复杂度

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define random(a,b)  (rand()%(b-a+1)+a)
typedef long long ll;
struct node
{
    int sum;//求两个序列任意元素和
    int index;//表示序列a的下标
};
bool operator < (node aa,node bb)
{
    return aa.sum>bb.sum;//降级,重载小于号
}
ll a[100005];
ll b[100005];
priority_queue<node>heep;
int main ( )
{
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
     int n;
     cin>>n;
     rep(i,1,n)
     cin>>a[i];
     rep(i,1,n)
     cin>>b[i];
     sort(a+1,a+1+n);
     sort(b+1,b+1+n);
     node nn;
     rep(i,1,n)
     {
         nn.index=1;
         nn.sum=a[1]+b[i];//这里先将所有的b元素与a的第一个元素相加,得到的第一个肯定是最优解,其他的继续重复该操作
         heep.push(nn);//这里先是预处理
     }
     while(n--)//看成队列的存取,其实就是运用优先队列的特点
     {
         nn=heep.top();
         heep.pop();
         cout<<nn.sum<<" ";
         nn.index++;
         nn.sum+=a[nn.index]-a[nn.index-1];
         heep.push(nn);
     }
     cout<<endl;
   return 0;
}

下面是建立大堆的方法: 

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
const int maxn=100005;
typedef long long ll;
int n;
ll a[2000005];
ll b[100005];
priority_queue<int> p;
int main()
{
    cin>>n;
    rep(i,1,n) cin>>a[i];
    rep(i,1,n) cin>>b[i];
    rep(i,1,n) p.push(a[1]+b[i]);
    rep(i,2,n)
    rep(j,1,n)
    {
        if(a[i]+b[j]>=p.top())
            break;//越往后越大,所以这是nlongnlogn的复杂度
        p.pop();//注意这里,是保证将当前的队列的元素最大值不断减小的步骤
        p.push(a[i]+b[j]);
    }
    memset(a,0,sizeof a);
    int tot=1;
    while(p.size())
        a[tot++]=p.top(),p.pop();
    for(int i=n;i>=1;i--)
        cout<<a[i]<<" ";
    cout<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/c___c18/article/details/81566361