题目描述
有两个长度都是N的序列A和B,在A和B中各取一个数相加可以得到 N^2N2 个和,求这 N^2N2 个和中最小的N个。
输入输出格式
输入格式:
第一行一个正整数N;
第二行N个整数 A_iAi , 满足 A_i\le A_{i+1}Ai≤Ai+1 且 A_i\le 10^9Ai≤109 ;
第三行N个整数 B_iBi , 满足 B_i\le B_{i+1}Bi≤Bi+1 且 B_i\le 10^9Bi≤109 .
【数据规模】
对于50%的数据中,满足1<=N<=1000;
对于100%的数据中,满足1<=N<=100000。
输出格式:
输出仅一行,包含N个整数,从小到大输出这N个最小的和,相邻数字之间用空格隔开。
输入输出样例
输入样例#1: 复制
3 2 6 6 1 4 8
输出样例#1: 复制
3 6 7
思路
此题很多种思路,这里只说两种
第一种数学
优化朴素算法,就是当i * j > n时就不是答案;
代码如下
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define maxn 100005
typedef long long ll;
using namespace std;
ll mod = 1e9 + 7;
ll a[maxn],b[maxn];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
ll n;
cin >> n;
for(int i = 1; i <= n ; i ++)
{
cin >> a[i];
}
for(int i = 1; i <= n ; i ++)
{
cin >> b[i];
}
priority_queue<ll>q;
for(int i = 1; i <= n; i ++)
{
for(int j = 1; j * i <= n; j ++)
{
q.push( -a[i] - b[j]);
}
}
for(int i = 1; i <= n; i ++)
{
cout << -q.top() << ' ';
q.pop();
}
return 0;
}
第二种 二分
枚举一个中间值,找出所有数相加小于这个中间的有多少个;
大于n的话那么就枚举更小的
小于n的话就枚举更大的;
代码如下
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define maxn 100005
typedef long long ll;
using namespace std;
ll mod = 1e9 + 7;
ll a[maxn],b[maxn],q[maxn];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
ll n;
cin >> n;
for(int i = 1; i <= n ; i ++)
{
cin >> a[i];
}
for(int i = 1; i <= n ; i ++)
{
cin >> b[i];
}
ll l = a[1] + b[1];
ll r = a[n] + b[n];
while(l < r)
{
ll mid = l + (r - l) / 2;
ll sum = 0;
ll j = n;
for(int i = 1; i <= n; i ++)
{
while(a[i] + b[j] > mid && j > 0)j--;
sum += j;
}
if(sum < n)l = mid + 1;
else r = mid;
}
ll j = n;
ll cnt = 0;
for(int i = 1; i <= n; i ++)
{
while(a[i]+b[j] >= l && j > 0)j--;
for(int k = 1; k <= j; k ++)
{
q[++cnt] = a[i] + b[k];
}
}
for(int i = cnt + 1; i <= n; i ++)
{
q[i] = l;
}
sort(q+1,q+n+1);
for(int i = 1; i <= n; i ++)
{
cout << q[i] << ' ';
}
cout << endl;
return 0;
}