题目描述
难度分:1700
输入T(≤104)表示T组数据。所有数据的n之和≤2×105,m之和≤2×105。
每组数据输入n(1≤n≤2×105),m(1≤m≤2×105)和长为n的数组a(1≤a[i]≤109),长为m的数组b(1≤b[i]≤109)。
把b中所有元素以任意顺序在任意位置插入a中,得到数组c。例如a=[3,1,1],b=[9,4,5],插入后可以是c=[3,1,4,1,5,9]。
你需要使LIS(c)最短(LIS是最长严格递增子序列),输出任意符合要求的c。
输入样例
7
2 1
6 4
5
5 5
1 7 2 4 5
5 4 1 2 7
1 9
7
1 2 3 4 5 6 7 8 9
3 2
1 3 5
2 4
10 5
1 9 2 3 8 1 4 7 2 9
7 8 5 4 6
2 1
2 2
1
6 1
1 1 1 1 1 1
777
输出样例
6 5 4
1 1 7 7 2 2 4 4 5 5
9 8 7 7 6 5 4 3 2 1
1 3 5 2 4
1 9 2 3 8 8 1 4 4 7 7 2 9 6 5
2 2 1
777 1 1 1 1 1 1
算法
贪心构造
挺有思维难度的一道题,代码很短,但是需要好好思考。首先可以明确的是,由于a不让动,所以c的LIS是绝对不可能比a短的,不要抱有这个不切实际的幻想。那么我们就希望把b的元素插入到a后,LIS的增量最小,那必然存在一个最优解是把b降序排列后按顺序插入到a的元素间隙中去。
接下来就比较直觉了,对于一个递增序列中的某个元素num,我们肯定是希望把比它大的元素按照降序插入在它前面,这样增加的就是降序的长度,不会增加num所在递增序列的长度。所以i指针遍历a,同时j指针遍历降序的b,对于某个元素a[i],先往c数组中追加大于a[i]的b[j],右移j指针(如果b还没有遍历完的话),然后再追加a[i],右移i指针。遍历完a数组后,如果b还没有遍历完,就继续把剩余元素追加到c中。
复杂度分析
时间复杂度
对b排序的时间复杂度为O(mlog2m),然后构造c数组是个双指针算法,遍历完了a数组和b数组,时间复杂度为O(n+m)。因此,整个算法的时间复杂度为O(mlog2m+n+m)。
空间复杂度
c这个答案数组的空间是可以省掉的,可以在双指针的运行过程中直接打印。因此算法的空间瓶颈在于排序,以快排为基准,额外空间复杂度就是O(log2m)。如果是堆排序,额外空间复杂度可以是O(1)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
int t, n, m, num;
int main() {
scanf("%d", &t);
for(int cases = 1; cases <= t; cases++) {
scanf("%d%d", &n, &m);
vector<int> a, b;
for(int i = 0; i < n; i++) {
scanf("%d", &num);
a.push_back(num);
}
for(int i = 0; i < m; i++) {
scanf("%d", &num);
b.push_back(num);
}
sort(b.begin(), b.end());
int j = m - 1;
for(int i = 0; i < n; i++) {
while(j >= 0 && b[j] > a[i]) {
printf("%d ", b[j--]);
}
printf("%d ", a[i]);
}
while(j >= 0) {
printf("%d ", b[j--]);
}
puts("");
}
return 0;
}