关于求排列分解为多个上升子序列的最小划分方案
做 ABC 468 F - Chmax 读错题(以为是要求最大化选择 \(2\) 个不交上升子序列总长度)+猜错结论得到(本以为下文可以解决最大化选择 \(k\) 个不交上升子序列总长度,然而随手举了个反例就炸了)
排列指序列没有重复数字。贪心维护多个不交上升子序列及其末尾元素。对于新元素,找到末尾元素最大且小于新元素的子序列加入;若不存在则新建一个长度为 \(1\) 的子序列。时间复杂度 \(O(n \log n)\)。
#include<iostream>
#include<vector>
#include<map>
using namespace std;
int n;
map<int,vector<int>>mp;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
int p;cin>>p;
auto it=mp.lower_bound(p);
if(it==mp.begin()) mp.insert({p,vector<int>{p}});
else{
it--;
vector<int> tmp=move(it->second);
tmp.push_back(p);
mp.insert({p,tmp});
mp.erase(it);
}
}
cout<<mp.size()<<endl;
for(auto v:mp){
for(int i:v.second) cout<<i<<' ';
cout<<endl;
}
return 0;
}
感性证明
考虑两种操作:加入和新建。
当已有序列 \(S\),\(\mathit{last_S} < e\),元素 \(e\) 能够加入序列 \(S\) 时,若加入则划分数不变,新建则划分数增加 \(1\)。考虑之后元素 \(f\) 的加入:
- 如果 \(f > e\),则 \(f\) 能够直接加入到 \(e\) 之后而不改变划分数,加入 \(e\) 优于新建;
- 如果 \(\mathit{last_S} < f < e\),则 \(f\) 不能加入到 \(e\) 之后。这时 \(e\) 加入 \(S\),新建 \(f\) 和 \(f\) 加入 \(S\),新建 \(e\) 对划分数贡献一样。
- 如果 \(f< \mathit{last_S} < e\),则 \(f\) 必须新建。加入 \(e\) 优于新建。
序列更多时类似,所以元素 \(e\) 能够加入序列 \(S\) 时选择加入一定不劣于最优方案。
再考虑已有两个序列 \(S_1, S_2\) 的情况,同样考虑 \(f\) 的加入:
- 若 \(\mathit{last_{S_1}} < f < \mathit{last_{S_2}} < e\),则当选择 \(S_1 \leftarrow S_1 \cup \{e\}\) 时,\(f\) 必须新建,导致划分数增加 \(1\);
- 反之选择 \(S_2 \leftarrow S_2 \cup \{e\}\),则可以使划分数不变。其他情况选择 \(S_2 \leftarrow S_2 \cup \{e\}\) 也都不劣。
序列更多时类似,所以元素 \(e\) 能够加入末尾元素最大的序列 \(S\) 时选择加入一定不劣于最优方案。
若元素 \(e\) 无法加入任何序列,类似前文的讨论,选择新建序列一定不劣于改变之前的划分。于是可以证明贪心策略是最优解。
貌似朝前考虑会更好,但先这样吧。

浙公网安备 33010602011771号