woj1208 Sherlock's Code

题目链接:

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;
}
点赞