洛谷P1631 序列合并解题报告

题目描述

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

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

第二行 N N N个整数 A i A_i Ai, 满足 A i ≤ A i + 1 A_i\le A_{i+1} AiAi+1

第三行 N N N个整数 B i B_i Bi , 满足 B i ≤ B i + 1 B_i\le B_{i+1} BiBi+1

【数据规模】

对于50%的数据中,满足 1 ≤ N ≤ 1000 1\le N\le 1000 1N1000

对于100%的数据中,满足 1 ≤ N ≤ 1000 1\le N\le 1000 1N1000

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

输入输出样例
输入 
3
2 6 6
1 4 8
输出 
3 6 7

题目分析:
暴力枚举,复杂度 n 2 n^2 n2 会超时。
注意到 a [ i ] + b [ j ] ≤ a [ i ] + b [ j + 1 ] a[i] + b[j] \le a[i] + b[j + 1] a[i]+b[j]a[i]+b[j+1] , a [ i ] + b [ j ] ≤ a [ i + 1 ] + b [ j ] a[i] + b[j] \le a[i + 1] + b[j] a[i]+b[j]a[i+1]+b[j]
也就是 i + j i + j i+j (下标的和)更小的值更小,所以我们可以斜着枚举 (按下标的和枚举),枚举到2*n个就差不多退出循环。
在这里插入图片描述

代码:

#include<stdio.h>
#define fa(x) (x>>1)
#define ls(x) (x<<1)
#define max(a, b) (a > b ? a : b)
const int MAXn = 1e5 + 5;
struct BigRootHeap{
	int h[MAXn], size;
	void insert(int num){
		int x = ++size;
		while(fa(x) >= 1 && num > h[fa(x)])h[x] = h[fa(x)], x = fa(x);
		h[x] = num;
		return;
	} 
	void pop(){
		if(size <= 0)return;
	int x = ls(1);
	int num = h[size--];
	while(x <= size ){
	if(x + 1 <= size && h[x + 1] > h[x])x++;
	if(h[x] > num)h[fa(x)] = h[x], x = ls(x); 
	else break;
	}
	h[fa(x)] = num;
	return;
	}
}hp;
int a[MAXn], b[MAXn], n, ans[MAXn];
int main(){
	int tot = 0;
	scanf("%d", &n);
	for(int i = 1; i <= n; i++){
		scanf("%d", a + i);
	}
	for(int i = 1; i <= n; i++){
		scanf("%d", b + i);
	}
	
	int flag = 0, cnt = 0;
for(int k = 2; k <= 2*n; k++){
	for(int i = 1; i <= k -1; i++){
		int j = k - i;
			if(hp.size < n)hp.insert(a[i] + b[j]);
			else if(hp.size == n){
				hp.insert(a[i] + b[j]);
				hp.pop();
				flag++;
			}
	}
	if(flag > n)break;
}
	for(int i = n; i >= 1; i--){
		ans[i] = hp.h[1];
		hp.pop();
	}
	for(int i = 1; i <= n; i++){
		printf("%d ", ans[i]);
	}
	return 0;
} 

上面用到了堆的有关知识:
手写堆简单介绍

用STL 优先队列的版本,不过要慢400ms左右

#include<stdio.h>
#include<queue>
#include<functional>
#include<vector>
const int MAXn = 1e5 + 5;
std::priority_queue< int, std::vector<int> > q;
int n, a[MAXn], b[MAXn], ans[MAXn];
int main(){
	scanf("%d", &n);
	for(int i = 1; i <= n; i++){
		scanf("%d", a + i); 
	}
	for(int i = 1; i <=n; i++){
		scanf("%d", b + i); 
	}
	int flag = 0;
	for(int k = 2; k <= 2*n; k++){//枚举 i + j 的和 ,也就是斜着枚举 
		for(int i = 1; i <= k - 1; i++){
			int j = k - i;
			if(q.size() < n)q.push(a[i] + b[j]);
			else {
				q.push(a[i] + b[j]);
				q.pop();
				flag++;
			}
		}
		if(flag > n)break;
	}
	for(int i = n; i >= 1; i--){
		ans[i] = q.top();q.pop();
	}
	for(int i = 1; i <= n; i++){
		printf("%d ", ans[i]);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值