LIS相关问题及简单Dilworth定理

LIS相关问题及简单Dilworth定理


这里以一道经典的题目为例

题目描述

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

输入导弹依次飞来的高度,计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入格式

一行,若干个整数,中间由空格隔开。

输出格式

两行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。


问题一:

经典的动态规划问题,在给出的导弹高度序列中选取最长的不上升子序列,并输出长度

vector<int> m(100010);
int len=0;
m[0]=1e9;
for (int i=1;i<n;i++){
if (a[i]<=m[len]){
m[++len]=a[i];
}else{
auto iter=lower_bound(m.begin(),m.begin()+len+1,a[i],[](int x,int y){return x>=y;});
*iter=a[i];
}
}
cout<<len<<endl;

采用记录序列二分查找的方式优化时间复杂度至 \(O(n\log n)\)

注意的,lower_bound 默认为查找大于等于参数 \(k\) 的位置,范围该位置的迭代器

不同于最长上升子序列,这道题目中需要记录最长不上升子序列,所以 lower_bound 应查找小于等于参数 \(k\) 的位置

这里采用 lambda 表达式,匿名定义 cmp 函数

auto iter=lower_bound(m.begin(),m.begin()+len+1,a[i],[](int x,int y){return x>=y;});

等效于

bool cmp(int x,int y){
return x>=y;
}
auto iter=lower_bound(m.begin(),m.begin()+len+1,a[i],cmp);

问题二:

翻译过来的意思为在给定的导弹高度序列中,求出覆盖完所有元素的不上升子序列的最少个数

可以采用最小链覆盖问题的模型,Dilworth定理来解决

该定理断言:对于任意有限偏序集,其最大反链中元素的数目必等于最小链划分中链的数目。此定理的对偶形式亦真,它断言:对于任意有限偏序集,其最长链中元素的数目必等于其最小反链划分中反链的数目,由偏序集 \(P\) 按如下方式产生的图 \(G\) 称为偏序集的可比图:\(G\) 的节点集由 \(P\) 的元素组成,而 \(e\)\(G\) 中的边,仅当 \(e\) 的两端点在 \(P\) 中是可比较的,有限全序集的可比图为完全图——**百度百科*

因为涉及到组合数学与离散数学基础,这里不详尽解释该定理,也不会过多地验证它的正确性

对于这道题目而言,这个定理给出了一个重要的结论:

覆盖完所有元素的不上升子序列的最少个数(最小链覆盖)即为最长上升子序列的长度

vector<int> dm(100010);
len=0;
dm[0]=0;
for (int i=1;i<n;i++){
if (a[i]>dm[len]){
dm[++len]=a[i];
}else{
auto iter=lower_bound(dm.begin(),dm.begin()+len+1,a[i],[](int x,int y){return x<y;});
*iter=a[i];
}
}
cout<<len<<endl;
posted @   才瓯  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示