题目描述
有两个长度都是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} Ai≤Ai+1
第三行
N
N
N个整数
B
i
B_i
Bi , 满足
B
i
≤
B
i
+
1
B_i\le B_{i+1}
Bi≤Bi+1
【数据规模】
对于50%的数据中,满足 1 ≤ N ≤ 1000 1\le N\le 1000 1≤N≤1000;
对于100%的数据中,满足 1 ≤ N ≤ 1000 1\le N\le 1000 1≤N≤1000;
输出格式
输出仅一行,包含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;
}