Dilworth定理学习笔记

前言:昨天膜你赛t2中考到了dilworth定理,感觉有一点点奇妙,所以简单学习了一下


1.引入:首先回顾昨天膜你赛t2

è¿éåå¾çæè¿° 

题目大意是给定n个二元组(x,y),对于x1<=x2且y1<=y2的两个二元组可分为一堆,问最少分多少堆

思路:将所有二元组按x从大到小排序后,那么只要看y最小能划分成多少个不上升子序列即可

问题来了,怎么求出最小划分数呢?

——Dilworth定理闪亮登场

2.什么是dilworth定理?

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

 (可以简易理解为:最小划分数=最长反链长度

证明:

定理 对偏序集<A,≤>,设A中最长链的长度是n,则将A中元素分成不相交的反链,反链个数至少是n。

证明施归纳于n。

当n=1时,A本身就是一条反链,定理结论成立。(这时≤是恒等关系)。 

假设对于n=k,结论成立。考虑n=k+1的情况,当A中最长链的长度为k+1时,令M为A中极大元的集合,显然M是一条反链。而且A-M中最长链的长度为k。由归纳假设,可以把A-M分成至少k个不相交的反链,加上反链M,则A可分成至少k+1条反链。(来自百度百科)

所以,对于刚才那道题,只需求最长上升子序列即可

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,f[100005],a[100005],len,x[100005],xx,yy,cnt;
struct node{int x,y;}b[100005];
bool cmp(node a,node b){
	if(a.x!=b.x)return a.x>b.x;
	else return a.y>b.y;
}
bool vis[1005][1005];
signed main(){
	scanf("%lld",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld%lld",&xx,&yy);
		if(!vis[xx][yy]){
			b[i].x=xx;b[i].y=yy;
			vis[xx][yy]=1;
			cnt++;
		}
	}n=cnt;
	sort(b+1,b+1+n,cmp);
	for(int i=1;i<=n;i++)a[i]=b[i].y;
   for(int i=1;i<=n;i++){
        if(len==0||a[i]>x[len]) x[++len]=a[i];
        else{
            int l=1,r=len;
            while(l<r){
                int mid=(l+r)>>1;
                if(a[i]<=x[mid]) r=mid;
                else l=mid+1;
            }
            x[l]=a[i];
        }
    }
    printf("%lld\n",len);
	return 0;
}

3.典型例题

(1) [USACO14DEC]Cow Jog G - 洛谷

题目大意:给n只奶牛,给定初始位置(各不相同且保证升序输入),每只奶牛速度,求最少安排多少跑道保证不会发生超车

分析:首先,由于初始位置各不相同且升序,若t时间后结束位置有逆序,说明过程中存在超车,故此题的实质是要求对于结束位置的最小上升子序列划分数,根据dilworth定理,可以转化为求最长不上升子序列

#include <bits/stdc++.h>
#define int long long 
using namespace std;
int a[1000005],c[1000005],len,p,n,t,v;
signed main(){
	scanf("%lld%lld",&n,&t);
	for(int i=1;i<=n;i++)scanf("%lld%lld",&p,&v),a[i]=p+v*t;
	for(int i=n;i>=1;i--){
		if(len==0||a[i]>=c[len])c[++len]=a[i];
		else{int t=upper_bound(c+1,c+len+1,a[i])-c;
			c[t]=a[i];
		}
	}
	printf("%lld\n",len);
	return 0;
}

 (2)[TJOI2015]组合数学 - 洛谷

#include <bits/stdc++.h>
#define int long long 
using namespace std;
int a[1005][1005],f[1005][1005],t,n,m;
signed main(){
	scanf("%d",&t);
	while(t--){
		memset(f,0,sizeof(f));
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				scanf("%d",&a[i][j]);
			}
		}
		for(int i=1;i<=n;i++){
			for(int j=m;j>=1;j--){
				f[i][j]=max(max(f[i-1][j],f[i][j+1]),f[i-1][j+1]+a[i][j]);
			}
		}
		printf("%lld\n",f[n][1]);
	}
	return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值