写题时遇到一个计算导弹拦截系统的题解使用了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;
}