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:
3
2 6 6
1 4 8
输出样例#1:
3 6 7
题意很明确,就是求出合并后的最小值,但要注意的是,两组数据是有重复的数字,但是输出是不需要去重的,有重复的也是可以
思路:用最暴力的方法只能过一半的数据,我用过map去重再排序,但是还是w了,代码也比较繁琐,看了一些博客,知道了原来是可以用堆排序(这里使用最小堆),里面用到了重载,priority_queue
需要预处理一下ab序列,从小到大排一次序,保证后面的操作是最优的,然后就是将a[1]分别于b中的元素进行相加,然后加入到最小堆中,这样堆的首元素就是我们所需要的答案,然后循环n次每次又要进行同样的操作,不过要注意对下一个元素的处理
参考
小堆的耗时少nlogn的复杂度
#include
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_queueheep;
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<
下面是建立大堆的方法:
#include
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 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<