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掉最小值时,用堆顶推出组内的下一个元素.

 

posted @ 2021-01-05 11:16  acmloser  阅读(71)  评论(0编辑  收藏  举报