Dilworth定理总结

**~~

Dilworth定理总结

**~~
定理内容:偏序集能划分成的最少的全序集个数等于最大反链的元素个数/最长反链=最小链覆盖

相关解释
偏序:定义集合A中的一个二元关系≤,对于两个元素(a1,b1)和(a2,b2),可以定义(a1,b1)≤(a2,b2),当且仅当a1≤a2且b1≤b2。若二者有一不满足,则两元素不可比
满足以下三个条件时,(A,≤)是偏序集:
1.自反性:∀a∈A,a≤a

2.反对称性:∀a,b∈A,若a≤b,b≤a则a=b。

3.传递性:∀a,b,c∈A,若a≤b,b≤c,则a≤c。
全序集:设≤为非空集合A上的一个偏序关系,若对于集合∀a,b∈B都有b≤a或a≤b(即元素两两可比),就称(B,≤)为一个全序集。
反链:偏序集(B,≤)中任意元素两两不可比。

例题:
1.P1020 有一种存在缺陷导弹拦截系统:、它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。输入导弹依次飞来的高度。计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
第一问裸LIS。
对第二问,设每个元素为二元组(a,b) a为到达时间,b为高度。令偏序关系<=为 a1<a2 &&b1>=b2。问最少划分为多少全序集,也就是求最长反链长度,即LIS元素个数。

const int MAX_N=1e5+5;
int n,a[MAX_N],b[MAX_N];
int ans[MAX_N],len;
int LIS(int *a)
{
	ans[1]=a[1],len=1;
	repi(i,2,n){
		if(a[i]>ans[len])	ans[++len]=a[i];
		else ans[lower_bound(ans+1,ans+len,a[i])-ans]=a[i];
	}
	return len;
}
int LIS_2(int *a)
{
	ans[1]=a[1],len=1;
	repi(i,2,n){
		if(a[i]>=ans[len])	ans[++len]=a[i];
		else ans[upper_bound(ans+1,ans+len,a[i])-ans]=a[i];
	}
	return len;
}
int main()
{
	int pos=0,x;
	while(~si(x)) a[++pos]=x;
	n=pos;
	repi(i,1,n) b[pos--]=a[i];
	printf("%d\n%d\n",LIS_2(b),LIS(a));
	return 0;
}

2.P4298 给定一张DAG,选取几个点,要求在选定的任意两点x,y,不存在x->y或y->x的路径。问 1.最多选几个点 2.给出一种构造方案 3.在最多选择的情况下,哪些点可以被选
令两点的偏序关系为x≤y为y到x存在路径
问题即为求所给DAG的最长反链。(任意两点不存在到对方的路径)
最长反链=最小不可重链覆盖
对于DAG图,也可当作最小可重链覆盖/最大独立集。对二分图,最大独立集为最小点覆盖的补集,而二分图最小点覆盖=最大匹配,答案即为点数-最大匹配。
具体做法:先floyd求出传递闭包,拆点为二分图,x->y存在边,二分图中
x1->y2连边。最长反链=最小链覆盖=点数-最大匹配数
对第二问,构造最一最大独立集,可先构造一最小点覆盖。后求其补集即可。
具体做法:从右侧非匹配点出发dfs。右侧的点只能走非匹配边向左访问,左侧的点只能走匹配边向右访问。取左侧被 DFS 到的点,以及右侧没被 DFS 到的点,做集合 S,可以证明 S 是一个最小点覆盖。(此处不予证明)。最大独立集等于最小点覆盖的补集。也就是只要选出左侧没被 DFS 到的点和右侧被 DFS 到的点就行了。
对DAG图,令最大独立集为I,选出所有左右两侧均在I的点记作集合A,构成最长反链
即 左侧没被dfs到 和 右侧被dfs到 的点。
第三问:枚举选定一点作为最长反链的一点,删除与其具有偏序关系的点,求剩下的点的最长反链与初始的最长反链是否差1即可

const int MAX_N=2e2+5;
const int MAX_E=1e4+5;
const int inf=INT_MAX;
int n,m;
int g[MAX_N][MAX_N];
struct Edge{
	int to,nxt;
};
struct Bipartite_matching{
	int nl,nr;
	Edge e[MAX_E<<1];
	int head[MAX_N],tote;
	int match[MAX_N];
	bool used[MAX_N],vis[MAX_N],ban[MAX_N];
	void init(int l,int r)
	{
		nl=l,nr=r,tote=0;
		repi(i,1,nl+nr)	head[i]=0;
	}
	void add_edge(int u,int v)
	{
		e[++tote].to=v,e[tote].nxt=head[u],head[u]=tote;
		e[++tote].to=u,e[tote].nxt=head[v],head[v]=tote;
	}
	bool dfs(int u)
	{
		if(ban[u]) return false;
		used[u]=true;
		reps(u)if(!ban[e[i].to]){
			int v=e[i].to,w=match[v];
			if(!w||!used[w]&&dfs(w)){
				match[u]=v,match[v]=u;
				return true;
			}
		}
		return false;
	}
	int cal()
	{
		int res=0; ms(match);
		repi(i,1,nl)if(!ban[i]){
			if(!match[i]){
				ms(used);
				if(dfs(i))	res++;
			}
		}
		return res;
	}
	void dfs_2(int u,int flag)
	{
		vis[u]=true;
		reps(u)if(!vis[e[i].to]&&((match[e[i].to]==u)==flag)) dfs_2(e[i].to,flag^1);
	}
}bm;
void floyd(int n)
{
	repi(k,1,n)repi(i,1,n)repi(j,1,n)
		g[i][j]=(g[i][k]&&g[k][j])?1:g[i][j];
} 
void init(int n)
{
	repi(i,1,n)repi(j,1,n) g[i][j]=0;
}
int main()
{
	si(n),si(m);
	init(n);
	repi(i,1,m){
		int u,v; si(u),si(v);
		g[u][v]=1;
	}
	floyd(n);
	bm.init(n,n);
	repi(i,1,n)repi(j,1,n)if(g[i][j]) bm.add_edge(i,j+n);
	int ans=n-bm.cal();
	printf("%d\n",ans);
	ms(bm.vis);
	repi(i,1,n)if(!bm.match[i+n]) bm.dfs_2(i+n,0);
	repi(i,1,n) printf("%d",(!bm.vis[i]&&bm.vis[i+n])?1:0);
	puts("");
	repi(i,1,n){
		ms(bm.ban);
		int tot=n;
		repi(j,1,n)if(g[i][j]||g[j][i]||i==j) bm.ban[j]=bm.ban[j+n]=true,tot--;
		printf("%d",tot-bm.cal()==ans-1);
	}
	puts("");
	return 0;
}

3.ICPC Latin American Regional Contests-A 给一些集合,现从每个集合中选出一些子集,使得被挑选的集合不是包含关系。
令偏序关系≤为包含关系,问题即为求最长反链元素个数。最长反链=最小不可重链覆盖。对所给集合拆点建二分图。对x≤y≤z,只连z-y和y-x的边。最后点数-最大匹配即可。(注意与上题区分,上题是在DAG图的情况下)

const int MAX_N=3e5+5;
const int MAX_E=5e6+5;
struct Edge{
	int to,nxt;
};
struct Bipartite_matching{
	int nl,nr;
	Edge e[MAX_E<<1];
	int head[MAX_N],tote;
	int match[MAX_N];
	bool used[MAX_N];
	void init(int l,int r)
	{
		nl=l,nr=r,tote=0;
		repi(i,1,nl+nr)	head[i]=0;
	}
	void add_edge(int u,int v)
	{
		e[++tote].to=v,e[tote].nxt=head[u],head[u]=tote;
		e[++tote].to=u,e[tote].nxt=head[v],head[v]=tote;
	}
	bool dfs(int u)
	{
		used[u]=true;
		reps(u){
			int v=e[i].to,w=match[v];
			if(!w||!used[w]&&dfs(w)){
				match[u]=v,match[v]=u;
				return true;
			}
		}
		return false;
	}
	int cal()
	{
		int res=0; ms(match);
		repi(i,1,nl){
			if(!match[i]){
				ms(used);
				if(dfs(i))	res++;
			}
		}
		return res;
	}
}bm;
map<string,int> nam;
int id=0;
map<vector<int>,int> rec;
int tot=0;
int cnt[(1<<10)+5];
int main()
{
	repi(i,0,(1<<10)) cnt[i]=__builtin_popcount(i);
	cin.tie(0),cout.tie(0);
	int n; si(n);
	repi(i,1,n){
		int m; si(m);
		vector<int> vec;
		repi(j,1,m){
			string s; cin>>s;
			if(!nam[s]) nam[s]=++id;
			vec.pb(nam[s]);
		}
		sort(vec.begin(),vec.end());
		if(rec[vec]) continue;
		int len=vec.size(),mask=(1<<len)-1;
		for(int sub=mask;sub;sub=(sub-1)&mask){
			vector<int> tmp;
			repi(j,0,len-1)if(sub&(1<<j)) tmp.pb(vec[j]);
			if(!rec[tmp]) rec[tmp]=++tot;
		}
	}
	bm.init(tot,tot);
	for(auto it:rec){
		vector<int> vec=it.fi;
		int len=vec.size(),mask=(1<<len)-1;
		for(int sub=(mask-1)&mask;sub;sub=(sub-1)&mask){
			if(cnt[mask]!=cnt[sub]+1) continue;
			vector<int> tmp;
			repi(j,0,len-1)if(sub&(1<<j)) tmp.pb(vec[j]);
			bm.add_edge(it.se,rec[tmp]+tot);
		}
	}
	printf("%d\n",tot-bm.cal());
	return 0;
}

好像之前还遇到过一个说是要dilworth定理的题,翻回去看发现前置技能还差些,再说吧
留坑待填

发布了6 篇原创文章 · 获赞 1 · 访问量 100

猜你喜欢

转载自blog.csdn.net/qq_44684888/article/details/105491879