Dilworth定理是个啥东东

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 a1a2 b 1 ≤ b 2 b_1 \leq b_2 b1b2,而若 a 1 > a 2 a_1>a_2 a1>a2 b 1 ≤ b 2 b_1 \leq b_2 b1b2,这两个元素就不可比了。

在满足以下三个条件时, ( A , ≤ ) (A,\leq) (A,)就是个偏序集:

1.自反性: ∀ a ∈ A , a ≤ a \forall a \in A,a \leq a aA,aa

2.反对称性: ∀ a , b ∈ A \forall a,b \in A a,bA,若 a ≤ b , b ≤ a a \leq b,b \leq a ab,ba,则 a = b a=b a=b

3.传递性: ∀ a , b , c ∈ A \forall a,b,c \in A a,b,cA,若 a ≤ b , b ≤ c a \leq b, b \leq c ab,bc,则 a ≤ c a \leq c ac

全序集

≤ \leq 为非空集合 A A A上的一个偏序关系,若对于集合 ∀ a , b ∈ B \forall a,b \in B a,bB,都有 a ≤ b a \leq b ab b ≤ a b \leq a ba(即元素两两可比),就称 ( 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 a1a2 b 1 ≥ b 2 b_1 \geq b_2 b1b2

第二问问的是最少被划分为多少全序集,也就是要求最长反链长度,也就是求两两满足满足 a 1 ≤ a 2 a_1 \leq a_2 a1a2 b 1 &lt; b 2 b_1 &lt; 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;
}
  • 27
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值