woj1208 Sherlock's Code
2016-01-21 23:37
369 查看
题目链接:
http://acm.whu.edu.cn/learn/problem/detail?problem_id=1208
题目的大概意思是输入两个长度为N的数组,计算数组的两两之和,得到N*N个数后,从小到大排序,输出前N个数。
题目的限制条件还是有的,数据的规模:数组的长度1 <= N <= 50000;空间限制:65536KB;时间限制是1s.
这道题卡了我很久。拿到题的第一想法就是,肯定不能直接按照题目的意思去做,因为这样做内存很容易超。需要另辟思路,第一个想法是用一个规模为N的堆(大根堆)去维护输出的结果。遍历计算,每次得到一个和值,就与堆顶的元素相比较,如果该和值比堆顶元素小,就将堆顶元素出堆,将该元素入堆,重新调整堆。当所有元素遍历完以后,再对这个大根堆进行一次排序,然后输出即可。
上面的算法复杂度还是比较高的,O(n2logn),提交超时也正常。还是沿着这种思路,稍微优化修改一下就行了。上面的代码比较粗糙。
这题的优化解法可以参考《算法导论》中k路归并算法,可以这样考虑,将a
和b
排序后,用b
中的每个元素去加a
中的一个元素,得到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
和b
,并将其升序排序,再将第一个有序表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代码如下:
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
和b
排序后,用b
中的每个元素去加a
中的一个元素,得到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
和b
,并将其升序排序,再将第一个有序表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 ; int num2 ; 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; }
相关文章推荐
- 书评:《算法之美( Algorithms to Live By )》
- 动易2006序列号破解算法公布
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- c语言实现的带通配符匹配算法
- 浅析STL中的常用算法
- 算法之排列算法与组合算法详解
- C++实现一维向量旋转算法
- Ruby实现的合并排序算法
- C#折半插入排序算法实现方法
- 基于C++实现的各种内部排序算法汇总
- C++线性时间的排序算法分析
- C++实现汉诺塔算法经典实例
- PHP实现克鲁斯卡尔算法实例解析