321. Create Maximum Number
问题:
给定两个int数组,代表两个数位数值的列表。
从两个数组中,按照已有的顺序选取数位数值,
组成一个新的数,长度为k
求能构成的数最大为?
Example 1: Input: nums1 = [3,4,6,5], nums2 = [9,1,2,5,8,3], k = 5 Output: [9,8,6,5,3] Example 2: Input: nums1 = [6,7], nums2 = [6,0,4], k = 5 Output: [6,7,6,0,4] Example 3: Input: nums1 = [3,9], nums2 = [8,9], k = 3 Output: [9,8,9] Constraints: m == nums1.length n == nums2.length 1 <= m, n <= 500 0 <= nums1[i], nums2[i] <= 9 1 <= k <= m + n
解法:Greedy + DP
分解问题为子问题:
- 两个数组中分别选一定数量,总共k个数后,这些数排列成最大的: merge
- 从nums1中选取 i 个数,maxArry
- 从nums2中选取 剩下的 k-i 个数。maxArry
- 能构成最大的数 res。
- 这个一定量(在两个数组中的分配) i 又可以是 0~k(不超过nums1.size,也不能使得超过nums2.size)
那么所求即为:
所有分配可能中,最大的结果。MAX<i=0~k>{merge(nums1[select i], nums2[select k-i])}
子问题 1: maxArry
在nums1中选取 k 个元素,使得到最大值
方法:堆栈弹出法
由于要求保持数列顺序,那我们尽可能将更大的值放在最前面。后面的值都小于前面的值,才能达到要求。
⚠️ 注意:但有可能后面的值都大于前面的值,那我们可不选的数量 pop_num=nums1.size-k 有限,pop完了这些限量后,不能再不选了,余留下来的,也只能保留,但也已经是最大了。
因此,我们从头遍历nums1.
若当前遍历到的数nums1[i]>入选的最后一个值 res.back
那么pop,res的最后一个值,再比较下一个res的最后值。
一直到nums[i]< res.back
则入栈res。
最后所得res,可能超过k,那我们只选择最前面的k个元素即可。res.resize(k);
1 //from array nums, choose k elements to make a maximum array 2 //which should keep the relative order. 3 vector<int> maxArry(vector<int>& nums, int k) { 4 int pop_nums = nums.size()-k; 5 if(pop_nums<=0) return nums; 6 vector<int> res; 7 for(int i=0; i<nums.size(); i++) { 8 while(!res.empty() && res.back()<nums[i] && pop_nums>0) { 9 res.pop_back(); 10 pop_nums--; 11 } 12 res.push_back(nums[i]); 13 } 14 res.resize(k); 15 return res; 16 }
子问题2: merge
合并nums1和nums2,使得获得最大序列。
方法:
遍历nums1和nums2的每一个元素。
每次选取 字典序 最大的一方的第一个元素 入res。
⚠️ 注意:6,7 VS 6,0,4
比较大的是 6,7
字典序的比较方法为,顶头开始比较,后面长度不足的,补零。
即上述,实际比较的是:670 VS 604
如果不用字典序比较,
若出现相同字符,无法判断入res的,选择哪一方。
但实际,我们需要选择,后面更大的一方,方便提前选择这一方的数字,成为更高数位,而使得总数值更大。
1 //merge 2 arrays to be a maximum array. 2 //which should keep the relative order. 3 vector<int> merge(const vector<int>& nums1, const vector<int>& nums2) { 4 auto s1 = nums1.cbegin(); 5 auto e1 = nums1.cend(); 6 auto s2 = nums2.cbegin(); 7 auto e2 = nums2.cend(); 8 vector<int> res; 9 // printvec1(res); 10 while(s1!=e1 || s2!=e2) { 11 res.push_back( 12 lexicographical_compare(s1,e1,s2,e2)? *s2++:*s1++ 13 ); 14 } 15 return res; 16 }
最后 遍历所有两个数列中分配选择数量的可能,选择最大。
代码参考:
1 class Solution { 2 public: 3 /* void printvec(vector<int>::const_iterator v, vector<int>::const_iterator& ve) { 4 cout<<"{"; 5 while(v!=ve){ 6 cout<< *v++ <<","; 7 } 8 cout<<"}"<<endl; 9 } 10 void printvec1(vector<int> v) { 11 cout<<"{"; 12 for(int ve:v){ 13 cout<< ve <<","; 14 } 15 cout<<"}"<<endl; 16 }*/ 17 vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) { 18 int n=nums1.size(), m=nums2.size(); 19 vector<vector<int>> dp(n+1, vector<int>(m+1,0)); 20 vector<int> res; 21 for(int i=0; i<=k; i++) { 22 if(i>n || k-i>m) continue; 23 res = max(res, merge(maxArry(nums1,i), maxArry(nums2, k-i))); 24 } 25 return res; 26 } 27 //merge 2 arrays to be a maximum array. 28 //which should keep the relative order. 29 vector<int> merge(const vector<int>& nums1, const vector<int>& nums2) { 30 auto s1 = nums1.cbegin(); 31 auto e1 = nums1.cend(); 32 auto s2 = nums2.cbegin(); 33 auto e2 = nums2.cend(); 34 vector<int> res; 35 // printvec1(res); 36 while(s1!=e1 || s2!=e2) { 37 res.push_back( 38 lexicographical_compare(s1,e1,s2,e2)? *s2++:*s1++ 39 ); 40 } 41 return res; 42 } 43 //from array nums, choose k elements to make a maximum array 44 //which should keep the relative order. 45 vector<int> maxArry(vector<int>& nums, int k) { 46 int pop_nums = nums.size()-k; 47 if(pop_nums<=0) return nums; 48 vector<int> res; 49 for(int i=0; i<nums.size(); i++) { 50 while(!res.empty() && res.back()<nums[i] && pop_nums>0) { 51 res.pop_back(); 52 pop_nums--; 53 } 54 res.push_back(nums[i]); 55 } 56 res.resize(k); 57 return res; 58 } 59 };
⚠️ 注意:
这里c++中,max函数,可以比较 vector