题目链接:
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;
}