Dilworth定理的简单应用(导弹拦截题解)

写题时遇到一个计算导弹拦截系统的题解使用了Dilworth定理,浅写下个人理解。

一.百科解释

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

名词概念

链 : D 中的一个子集 C 满足 C 是全序集 及C中所有元素都可以比较大小
反链 : D 中的一个子集 B 满足 B 中任意非空子集都不是全序集 即所有元素之间都不可以比较大小
链覆盖 : 若干个链的并集为 D ,且两两之间交集为 ∅
反链覆盖 : 若干个反链的并集为 D ,且两两之间交集为∅
最长链 : 所有链中元素个数最多的 (可以有多个最长链)
最长反链 : 所有反链中元素个数最多的 (可以有多个最长反链

emmm背景了解一下就行,至于证明我是一下午也没理解透。下面是我认为在简单的算法题中的使用。
所以Dilworth定理至少在算最长不上升子序列这种题还是很有用的。

二.Dilworth定理的个人理解

个人理解的反链:
简单说就是:>与<=,<与>=(部分情况可局限理解顺序与反序【注意“=”】)

结论:

链的最少划分数=反链的最长长度 (不是我自己总结的)

文字例子:

1:一个序列最少可以分成n个最长上升子序列,这个序列最长不上升子序列长度为m,则n=m
2:一个序列最少可以分成n个最长下降子序列,这个序列最长不下降子序列长度为m,则n=m

具体例子:

数列(1): 8 7 5 6 4 7 5 2
它可以分为n=3个最长下降子序列(如:{8 7 6 5 2},{5 4},{7})。
那么它的最长上升子序列就为m=n=3.
证明:上升子序列有{5 6 7}.,{4 7}以及长度为1的每个单个元素,可知上升子序列最长为3.
数列(2):8 5 2 7 6 4 3 1
它最少可以分为n=2个最长下降子序列,即{8 7 6 4 3 1}和{5 2}。
它最长不下降子序列长度为m=2,即5 7,5 6,2 7,2 6,2 4或2 3,反正没有一个长度超过2。

三.简单的实际使用

洛谷的一个二分题:https://www.luogu.com.cn/problem/P1020
在这里插入图片描述
该定理在该问题上可以理解成:把序列分成不上升子序列的最少个数,等于序列的最长上升子序列长度。把序列分成不降子序列的最少个数,等于序列的最长下降子序列长度。

很显然第一问我们需要一个数组 b,存储从第 1 个到第 n 个导弹的高度,然后再来一个数组 l,存储 最长不上升子序列(伪),还有一个变量 r1 代表 l 的结尾位置。使用upper_bound 依次比较即可。所以要注意的是,ll 中存储的并不是最大不上升子序列)。

第二问中就使用了Dilworth定理,只需计算最长上升子序列长度即可,由Dilworth定理可得列分成不降子序列的最少个数,等于序列的最长下降子序列长度。不用改比较器,而且因为是上升序列,所以等于也不行,得用 lower_bound。

这里我们用到2个算法函数lower_bound 与 upper_bound 。值得注意的是第一问因为从大到小查,upper_bound需要用到比较器。之前没见过这两个算法的可以看下图。
在这里插入图片描述###AC代码如下:

#include<bits/stdc++.h>
using namespace std;
#define man 100050
int n=0, r1,r2;
int b[man], l[man], h[man];
char q;
int main() {
	//手动输入测试的输入
	/*while ((cin >> b[++n]).get(q)) {
		if (q == '\n') {
			break;
		}
	}*/
	//提交时电脑文件输入的输入方式
	while (cin>>b[++n]);
	 --n;
	r1 = 1;
	l[r1] = b[r1];
	for (int i = 2; i <= n; ++i) {
		if (l[r1] >= b[i]) 
			l[++r1] = b[i];
		else 
			*upper_bound(l + 1, l + r1 + 1, b[i], greater<int>()) = b[i];
	}
	cout << r1 << endl;
	r2 = 1;
	h[r2] = b[r2];
	for (int i = 2; i <= n; ++i) {
		if (h[r2] < b[i]) 
			h[++r2] = b[i];
		else *lower_bound(h + 1, h + r2 + 1, b[i]) = b[i];
	}
	cout <<r2<< endl;
	return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值