CODEVS 1245 最小的N个和 堆+排序
原题链接
http://codevs.cn/problem/1245/
有两个长度为 N 的序列 A 和 B,在 A 和 B 中各任取一个数可以得到 N^2 个和,求这N^2 个和中最小的 N个。
第一行输入一个正整数N;第二行N个整数Ai 且Ai≤10^9;第三行N个整数Bi,
且Bi≤10^9
输出仅一行,包含 n 个整数,从小到大输出这 N个最小的和,相邻数字之间用
空格隔开。
5
1 3 2 4 5
6 3 4 1 7
2 3 4 4 5
【数据规模】 对于 100%的数据,满足 1≤N≤100000。
首先将数组A和B从小到大排序,令数组Sum[i]=A[i]+B[i]。
最开始的时候寻找满足0<=A[j]+B[k]<Sum[0]的j、k,将得到的和加入堆中,接下来寻找满足Sum[0]<=A[j]+B[k]<Sum[1]的j、k,将得到的和加入堆中。。。反复如此,直到堆的大小大于n。这样就将问题分为一段一段的求解。
现在的问题就在于如何寻找满足上述条件的j、k。
建一个数组tmp,对于A[j],当B[k]+A[j]>=Sum[i]的时候,tmp[j]=k并跳出。换句话说,tmp[j]表示A[j]在上一段的寻找中在B中停下的位置。
就拿样例来解释:
首先先对A,B排序:
A:1 2 3 4 5
B:1 3 4 6 7
然后求Sum:2 5 7 10 12
对于第一次,寻找小于等于2的A[j]+B[k],明显没有,所以直接讨论第二次,寻找小于5的A[j]+B[k],发现A[0]+B[2]>=5,所以tmp[0]=2,然后发现A[1]+B[1]>=5,所以tmp[1]=1,同理tmp[2]=1,tmp[3]=0,这时就没有继续在A上找下去的必要了。接下来要寻找小于7的A[j]+B[k],对于每一个A[j],都从B中tmp[j]中的位置开始寻找。复杂度是比O(n)稍大了一点,有个特殊情况是,Sum中的数个个相等,解决的办法是在A,B的末尾加一个很大的数。
详细见代码
#include<iostream> #include<cstring> #include<queue> #include<vector> #include<cstdio> #include<functional> #include<algorithm> #define MAX_N 100005 using namespace std; typedef long long ll; priority_queue<ll,vector<ll>,greater<ll> > que; int n; ll a[MAX_N],b[MAX_N]; ll sum[MAX_N],tmp[MAX_N]; int main() { memset(sum,0,sizeof(sum)); memset(tmp,0,sizeof(tmp)); scanf("%lld ",&n); for(int i=0;i<n;i++)scanf("%lld",&a[i]); for(int i=0;i<n;i++)scanf("%lld",&b[i]); sort(a,a+n);sort(b,b+n); a[n]=a[n-1]+100;b[n]=b[n-1]+100;n++; for(int i=0;i<n;i++)sum[i]=a[i]+b[i]; for(int i=0;i<n;i++) { ll s=sum[i]; for(int j=0;j<n;j++) { int t=tmp[j]; for(int k=t;k<n;k++) { ll sumTemp=a[j]+b[k]; if(sumTemp>=s){tmp[j]=k;break;} que.push(sumTemp); } if(tmp[j]==0)break; } if(que.size()>=n)break; } for(int i=0;i<n-1;i++) { printf("%lld ",que.top()); que.pop(); } cout<<endl; return 0; }