AcWing 146. 序列
考察: 堆排序+归并排序
错误思路:
将m个序列排序,堆储存,每个序列都选第一个,选完后pop掉堆顶最大的.
时间复杂度大概是O(mlogn+n*m),但是不能pop掉最大的,如果遇到 a {1,2,2} b{2,9,10}就是输出{3,10,11}的错误答案. 也就是a1+b2不一定比b1+a2小.
参考y总的思路:
参考归并排序的思想,将选出m个元素的最小值之和换成先选出2个、3个、4个元素和的最小值,也就是说我们在M个序列里归并两个序列,选出这两个序列的n个最小和,再依次往下归并.如果暴力的话时间复杂度和上面相同,因此我们可以对a数组先排个序,将b数组与a数组合并,选出其中N个最小值赋值a数组,再合并后面的b数组
这里利用了分组的思想
时间复杂度(n*(m-1)*logn)
1 #include <iostream> 2 #include <queue> 3 #include <vector> 4 #include <algorithm> 5 using namespace std; 6 typedef pair<int,int> pii; 7 const int N = 2010; 8 int a[N],b[N],c[N],t,m,n; 9 void merge() 10 { 11 priority_queue<pii,vector<pii>,greater<pii> >heap; 12 for(int i=1;i<=n;i++) heap.push({a[1]+b[i],1});//先初始化为a[1]+b[i] 13 int idx = 1; 14 while(idx<=n){ 15 auto it = heap.top(); 16 heap.pop(); 17 int x = it.first; int y = it.second; 18 c[idx++] = x; 19 heap.push({x-a[y]+a[y+1],y+1}); 20 } 21 for(int i=1;i<=n;i++) a[i] = c[i]; 22 } 23 int main() 24 { 25 26 scanf("%d",&t); 27 while(t--) 28 { 29 scanf("%d%d",&m,&n); 30 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 31 sort(a+1,a+n+1); m--; 32 while(m--) 33 { 34 for(int i=1;i<=n;i++) scanf("%d",&b[i]); 35 merge(); 36 } 37 for(int i=1;i<=n;i++) printf("%d ",a[i]); 38 printf("\n"); 39 } 40 return 0; 41 }
2021.3.5 本题二刷 结论是还是不会
由错误思路只能枚举所有的可能,但是一次性不可能枚举m个序列的和,由归并可以想到先求出求i个序列的和,每次归并i+1,最后a数组就是答案,因此一次性合并两个序列,但是二重循环枚举会TLE.将a数组排序后,以bi+aj的i相同分组.可以发现i相同的组内,必然是以升序排列.以此我们可以用堆存储每个序列的第一个数,pop掉最小值时,用堆顶推出组内的下一个元素.