Dilworth定理
Dilworth定理,一言以蔽之,偏序集能划分成的最少的全序集个数等于最大反链的元素个数。
这对于数学不好的人(如litble)来说,不是句人话。翻译一下几个概念:
偏序
偏序嘛,应该不是那么陌生的东西 ,所谓“时属九月,三维偏序” 。
定义集合 A A A中的一个二元关系 ≤ \leq ≤,譬如对于 ( a 1 , b 1 ) (a_1,b_1) (a1,b1)和 ( a 2 , b 2 ) (a_2,b_2) (a2,b2)两个元素,我可以定义 ( a 1 , b 1 ) ≤ ( a 2 , b 2 ) (a_1,b_1) \leq (a_2,b_2) (a1,b1)≤(a2,b2)当且仅当 a 1 ≤ a 2 a_1 \leq a_2 a1≤a2且 b 1 ≤ b 2 b_1 \leq b_2 b1≤b2,而若 a 1 > a 2 a_1>a_2 a1>a2, b 1 ≤ b 2 b_1 \leq b_2 b1≤b2,这两个元素就不可比了。
在满足以下三个条件时, ( A , ≤ ) (A,\leq) (A,≤)就是个偏序集:
1.自反性: ∀ a ∈ A , a ≤ a \forall a \in A,a \leq a ∀a∈A,a≤a
2.反对称性: ∀ a , b ∈ A \forall a,b \in A ∀a,b∈A,若 a ≤ b , b ≤ a a \leq b,b \leq a a≤b,b≤a,则 a = b a=b a=b。
3.传递性: ∀ a , b , c ∈ A \forall a,b,c \in A ∀a,b,c∈A,若 a ≤ b , b ≤ c a \leq b, b \leq c a≤b,b≤c,则 a ≤ c a \leq c a≤c。
全序集
设 ≤ \leq ≤为非空集合 A A A上的一个偏序关系,若对于集合 ∀ a , b ∈ B \forall a,b \in B ∀a,b∈B,都有 a ≤ b a \leq b a≤b或 b ≤ a b \leq a b≤a(即元素两两可比),就称 ( B , ≤ ) (B, \leq) (B,≤)为一个全序集。
反链
若偏序集 ( B , ≤ ) (B, \leq) (B,≤)中的元素两两不可比,则称 B B B为反链。
例题
洛谷P1020 导弹拦截
我们若设每一个元素为二元组 ( a , b ) (a,b) (a,b), a a a是这颗导弹的抵达时间, b b b是高度,那么令偏序关系 ≤ \leq ≤为: a 1 ≤ a 2 a_1 \leq a_2 a1≤a2且 b 1 ≥ b 2 b_1 \geq b_2 b1≥b2。
第二问问的是最少被划分为多少全序集,也就是要求最长反链长度,也就是求两两满足满足 a 1 ≤ a 2 a_1 \leq a_2 a1≤a2且 b 1 < b 2 b_1 < b_2 b1<b2的最大集合,也就是求最长严格下降子序列长度。
洛谷P4298/bzoj1143 祭祀
令偏序关系 ≤ \leq ≤为 a a a能到达 b b b,题问要求的是最长反链长度,就求最少被划分为多少全序集,也就是求最少路径覆盖。
至于输出方案呢,第一问是从每一个左侧未匹配点出发遍历匈牙利树,将沿途的所有点打上标记。如果一个原图中的点,在左边的分点打了标记,在右边的没打,就是一个祭祀点。至于为什么,litble太蠢暂未搞懂,希望大神在评论中指教,感激不尽。
第二问是枚举所有点,删除和它可比(可达它或它可达)的所有点,跑一遍最少路径覆盖,若只比原答案少1,则这个点可以作为祭祀点。
#include<bits/stdc++.h>
using namespace std;
#define RI register int
const int N=105;
int n,m,tot,ans,tim;
int h[N],ne[N*N],to[N*N],L[N][N],havcp[N],cp[N],vis[N];
int bj1[N],bj2[N],ban[N];
void floyed() {
for(RI k=1;k<=n;++k)
for(RI i=1;i<=n;++i)
for(RI j=1;j<=n;++j)
if(i!=k&&k!=j&&i!=j&&L[i][k]&&L[k][j]) L[i][j]=1;
}
void add(int x,int y) {to[++tot]=y,ne[tot]=h[x],h[x]=tot;}
int dfs(int x) {
bj1[x]=1;
for(RI i=h[x];i;i=ne[i])
if(vis[to[i]]!=tim&&!ban[to[i]]) {
vis[to[i]]=tim,bj2[to[i]]=1;
if(!cp[to[i]]||dfs(cp[to[i]]))
{havcp[x]=1,cp[to[i]]=x;return 1;}
}
return 0;
}
void print() {
for(RI i=1;i<=n;++i) bj1[i]=bj2[i]=0;
for(RI i=1;i<=n;++i) if(!havcp[i]) ++tim,dfs(i);
for(RI i=1;i<=n;++i) printf("%d",bj1[i]&&!bj2[i]);
puts("");
for(RI i=1;i<=n;++i) {
for(RI j=1;j<=n;++j) cp[j]=ban[j]=0;
int js=0;
for(RI j=1;j<=n;++j)
if(L[j][i]||L[i][j]||i==j) ban[j]=1;
else ++js;
for(RI j=1;j<=n;++j) {
if(ban[j]) continue;
++tim;if(dfs(j)) --js;
}
printf("%d",js==ans-1);
}
}
int main()
{
int x,y;
scanf("%d%d",&n,&m);
for(RI i=1;i<=m;++i) scanf("%d%d",&x,&y),L[x][y]=1;
floyed();
for(RI i=1;i<=n;++i)
for(RI j=1;j<=n;++j)
if(i!=j&&L[i][j]) add(i,j);
ans=n;
for(RI i=1;i<=n;++i) {++tim;if(dfs(i)) --ans;}
printf("%d\n",ans);
print();
return 0;
}