トピック:
長さmとnの2つの配列があり、0〜9の数字が2つの数値を表します。2桁の長さk <= m + nの最大数を作成します。同じ配列の数字の相対的な順序を保持する必要があります。k桁の配列を返します。
間違った解決策:動的計画法
質問は、組み合わせの数を最大化するために、m + n個の数からk個の数を選択することとして表現できます。01ナップサック問題は簡単に考えられます。組み合わせの数を最大化するには、「ナップサック容量」がkの数列から数を選択します。
しかし実際には、この問題には、01ナップサック問題の値とは異なる最適な部分構造がありません。01ナップサック問題で最初のk項目を最も価値のあるものにするソリューションでは、最初のk-1項目も作成する必要があります。番号の元の順序を維持して最大数を形成するという基準では、小規模問題の最適解を大規模問題の最適解の基礎として使用できないため、動的計画法ではできません。この問題に使用されます。
以下は私が踏んだピットです。
nums1
長さlen1
、nums2
長さlen2
、len=len1+len2
構造を使用して番号選択プランを表します。
struct selection{
vector<int> n1, n2;
};
- 選択状態
selection dp[i][x],0<=i<=len,0<=x<=k
はnums
、最初のi個の数(0〜i-1)からx個を取得するスキームを表し、ルールに従ってcreate()
生成されるx個の数が最大になります。 - 状態遷移方程式
- nums [i-1]を選択すると、一時的なスキームtemp1
- 0 <= i-1 <len1
一時的な計画は次のとおりです。
temp1 = dp[i-1][x-1]
temp1.n1.emplace_back(nums[i-1])
- len1 <= i <lenの
一時的な計画は次のとおりです。
temp1 = dp[i-1][x-1]
temp1.n2.emplace_back(nums[i-1])
- 0 <= i-1 <len1
- nums [i]が選択されていない場合、一時スキームtemp2
temp2 = dp[i-1][x]
- nums [i-1]を選択すると、一時的なスキームtemp1
dp[i][x] = judge(temp1, temp2) ? temp1 : temp2;
- 境界
保証i,x>=1
、0行目を初期化する必要があり、0列目は空のスキームです - 処理順序
は左上と上であり、処理順序は上から下、左から右です。
class Solution {
public:
struct selection{
vector<int> n1, n2;
};
vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
int len1=nums1.size(), len2=nums2.size(), len=len1+len2;
vector<int> ans;
if(len==0) return ans;
selection dp[len+1][k+1];
//填表
for(int i=1; i<=len; i++){
for(int x=1; x<=k; x++){
//若选nums[i-1]
selection temp1 = dp[i-1][x-1];
if(i-1<len1) temp1.n1.emplace_back(nums1[i-1]);
else temp1.n2.emplace_back(nums2[i-1-len1]);
//若不选nums[i-1]
selection temp2 = dp[i-1][x];
//判断temp1和temp2谁大
dp[i][x] = judge(temp1, temp2) ? temp1 : temp2;
}
}
create(dp[len][k], ans);
return ans;
}
bool judge(selection &a, selection &b){
//判断a是否比b更好
vector<int> an, bn;
create(a, an);
create(b, bn);
int sa=an.size(), sb=bn.size();
if(sa>sb) return true;
else if(sa<sb) return false;
else{
for(int i=0; i<sa; i++){
if(an[i]>bn[i]) return true;
else if(an[i]<bn[i]) return false;
}
return false;
}
}
void create(selection &a, vector<int> &an){
int t1=a.n1.size(), t2=a.n2.size(), p1=0, p2=0;
while(p1<t1 && p2<t2){
bool out = true;
int i;
for(i=0; p1+i<t1 && p2+i<t2; i++){
if(a.n1[p1+i]!=a.n2[p2+i]){
if(a.n1[p1+i]>a.n2[p2+i]) an.emplace_back(a.n1[p1++]);
else an.emplace_back(a.n2[p2++]);
out = false;
break;
}
}
if(out){
if(p1+i>=t1) an.emplace_back(a.n2[p2++]);
else an.emplace_back(a.n1[p1++]);
}
}
while(p1<t1) an.emplace_back(a.n1[p1++]);
while(p2<t2) an.emplace_back(a.n2[p2++]);
return;
}
};
解決策:数を選択する戦略があります
最大数を構成するスキームは、nums1とnums2の同じ条件下での最大数でなければなりません。
nums1と2を列挙して、いくつかの数値を取得します。
ACコード:
class Solution {
public:
vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
int len1=nums1.size(), len2=nums2.size();
vector<int> ans;
for(int n1=0; n1<=min(len1, k); n1++){
int n2 = k - n1;
if(n2>len2) continue;
vector<int> se1, se2, res;
getNumbers(nums1, se1, n1);
getNumbers(nums2, se2, n2);
create(se1, se2, res);
if(judge(res, ans)) ans = res;
}
return ans;
}
void getNumbers(vector<int>& nums, vector<int>& se, int k){
vector<int>::iterator s = nums.begin();
vector<int>::iterator e = nums.end() - k + 1;
while(k--){
vector<int>::iterator it = max_element(s, e);
se.emplace_back(*it);
s = it + 1;
e++;
}
return;
}
void create(vector<int>& a, vector<int>& b, vector<int>& res){
int t1=a.size(), t2=b.size(), p1=0, p2=0;
while(p1<t1 && p2<t2){
bool out = true;
int i;
for(i=0; p1+i<t1 && p2+i<t2; i++){
if(a[p1+i]!=b[p2+i]){
if(a[p1+i]>b[p2+i]) res.emplace_back(a[p1++]);
else res.emplace_back(b[p2++]);
out = false;
break;
}
}
if(out){
if(p1+i>=t1) res.emplace_back(b[p2++]);
else res.emplace_back(a[p1++]);
}
}
while(p1<t1) res.emplace_back(a[p1++]);
while(p2<t2) res.emplace_back(b[p2++]);
return;
}
bool judge(vector<int>& a, vector<int>& b){
int sa=a.size(), sb=b.size();
if(sa>sb) return true;
else if(sa<sb) return false;
else{
for(int i=0; i<sa; i++){
if(a[i]>b[i]) return true;
else if(a[i]<b[i]) return false;
}
return false;
}
}
};