题目链接:
http://acm.whu.edu.cn/learn/problem/detail?problem_id=1208
题目的大概意思是输入两个长度为N的数组,计算数组的两两之和,得到N*N个数后,从小到大排序,输出前N个数。
题目的限制条件还是有的,数据的规模:数组的长度1 <= N <= 50000;空间限制:65536KB;时间限制是1s.
这道题卡了我很久。拿到题的第一想法就是,肯定不能直接按照题目的意思去做,因为这样做内存很容易超。需要另辟思路,第一个想法是用一个规模为N的堆(大根堆)去维护输出的结果。遍历计算,每次得到一个和值,就与堆顶的元素相比较,如果该和值比堆顶元素小,就将堆顶元素出堆,将该元素入堆,重新调整堆。当所有元素遍历完以后,再对这个大根堆进行一次排序,然后输出即可。
#include<iostream> #include<stdio.h> #include<algorithm> #include<vector> using namespace std; int main() { freopen("in.txt","r",stdin); int n; while(cin>>n) { vector<int> va,vb,vheap; int temp; for(int i=0;i<n;++i) { cin>>temp; va.push_back(temp); } for(int i=0;i<n;++i) { cin>>temp; vb.push_back(temp); } for(int i=0;i<n;++i)//初始化堆 { temp=va[0]+vb[i]; vheap.push_back(temp); } make_heap(vheap.begin(),vheap.end()); for(int i=1;i<n;++i) { for(int j=0;j<n;++j) { int temp=va[i]+vb[j]; if(temp<vheap[0])//小于堆顶元素则重新调整堆 { vheap.erase(vheap.begin());//删除堆顶元素 vheap.push_back(temp);//temp入堆 make_heap(vheap.begin(),vheap.end()); } } } sort_heap(vheap.begin(),vheap.end()); cout<<vheap[0]; for(int i=1;i<n;++i) cout<<" "<<vheap[i]; cout<<endl; va.clear(); vb.clear(); vheap.clear(); } return 0; }
上面的算法复杂度还是比较高的,O(n2logn),提交超时也正常。还是沿着这种思路,稍微优化修改一下就行了。上面的代码比较粗糙。
这题的优化解法可以参考《算法导论》中k路归并算法,可以这样考虑,将a[n]和b[n]排序后,用b[n]中的每个元素去加a[n]中的一个元素,得到n个有序表,再把这n个有序表合并成一个有序表即可。
得到的n个有序表如下:
a[0]+b[0]<= a[0]+b[1]<= a[0]+b[2]<=…<=a[0]+b[n-1];
a[1]+b[0]<= a[1]+b[1]<= a[1]+b[2]<=…<=a[1]+b[n-1];
…
a[n-1]+b[0]<= a[n-1]+b[1]<= a[n-1]+b[2]<=…<=a[n-1]+b[n-1].
归并时,可以这样考虑,每个表的元素按序移入一个新表中,把每个表的当前元素放入一个二叉堆中,每次删除最小值并放入新表中,然后加入此序列的下一个元素,直到n个表遍历完。这种算法每次耗时log(n),n次共耗时nlog(n),所以AC掉这题是没有问题的。
具体的实现就是:读入a[n]和b[n],并将其升序排序,再将第一个有序表a[0] + b[i] ( 0<=i<=n-1)读入q向量中,维护一个大小为n的二叉堆。然后考虑第1个有序表,b[1] + a[i] (0<=i<=n-1),如果b[1] + a[i]比堆q的堆顶元素大,则退出,否则删除堆的堆顶元素,插入b[1] + a[i],依次计算其他有序表即可。再q的数据拷贝到a中,并对a按升序排序,输出a中的数据即可。
AC代码如下:
#include <iostream> #include <stdio.h> #include <queue> #include <algorithm> using namespace std; const int N=50000; int main() { freopen("in.txt","r",stdin); int n; int num1[N]; int num2[N]; priority_queue<int,deque<int>,less<int> > big; while(scanf("%d",&n)!=EOF) { for(int i=0;i<n;i++) scanf("%d",&num1[i]); sort(num1,num1+n); for(int j=0;j<n;j++) { scanf("%d",&num2[j]); big.push(num1[0]+num2[j]); } sort(num2,num2+n); for(int k=1;k<n;k++) for(int l=0;l<n;l++) { if(num1[k]+num2[l]>big.top()) break; big.pop(); big.push(num1[k]+num2[l]); } for(int k=0;k<n;k++) { num1[n-k-1]=big.top(); big.pop(); } printf("%d",num1[0]); for(int i=1;i<n;i++) printf(" %d",num1[i]); //printf("\n"); } return 0; }