poj1065



在这里先要介绍一下Dilworth定理:

分析:

第一问很简单,就是求最长不上升子序列。对于第二问不能用贪心的方法来做,因为有反例:7 5 4 1 6 3 2.

我们把第二问的问题抽象出来,那就是:把一个数列划分成最少的最长不升子序列,这里我们要介绍一个很优美的定理。

 

Dilworth定理:对于一个偏序集,最少链划分等于最长反链长度。

Dilworth定理的对偶定理:对于一个偏序集,其最少反链划分数等于其最长链的长度。

 

也就是说把一个数列划分成最少的最长不升子序列的数目就等于这个数列的最长上升子序列的长度。

 


下面来说说这个定理是怎么来的:

 

偏序集的定义:偏序是在集合X上的二元关系≤(这只是个抽象符号,不是“小于或等于”,它满足自反性、反对称性和传递

性)。即,对于X中的任意元素a,b和c,有:

 

(1)自反性:a≤a;

(2)反对称性:如果a≤b且b≤a,则有a=b;

(3)传递性:如果a≤b且b≤c,则a≤c 。

 

带有偏序关系的集合称为偏序集。

 

 


令(X,≤)是一个偏序集,对于集合中的两个元素a、b,如果有a≤b或者b≤a,则称a和b是可比的,否则a和b不可比。

在这个例子(反链)中元素Ri<=Rj是指(i<=j) and (ai>=aj)

 

一个反链A是X的一个子集,它的任意两个元素都不能进行比较。

一个链C是X的一个子集,它的任意两个元素都可比。

 

【定理】

在X中,对于元素a,如果任意元素b,都有a≤b,则称a为极小元。

定理1:令(X,≤)是一个有限偏序集,并令r是其最大链的大小。则X可以被划分成r个但不能再少的反链。

 

其对偶定理称为Dilworth定理:

令(X,≤)是一个有限偏序集,并令m是反链的最大的大小。则X可以被划分成m个但不能再少的链。

虽然这两个定理内容相似,但第一个定理证明要简单一些。此处就只证明定理1。

 

证明:设p为最少反链个数

(1)先证明X不能划分成小于r个反链。由于r是最大链C的大小,C中任两个元素都可比,因此C中任两个元素都不能属于同一反

链。所以p>=r。

(2)设X1=X,A1是X1中的极小元的集合。从X1中删除A1得到X2。注意到对于X2中任意元素a2,必存在X1中的元素a1,使得

a1<=a2。令A2是X2中极小元的集合,从X2中删除A2得到X3……,最终会有一个Xk非空而Xk+1为空。于是A1,A2,…,Ak就是X的

反链的划分,同时存在链a1<=a2<=…<=ak,其中ai在Ai内。由于r是最长链大小,因此r>=k。由于X被划分成了k个反链,因此

r>=k>=p。

(3)因此r=p,定理1得证。

 

 

Dilworth定理的应用  poj 1065

 

题意:给定n个二元组,设第一个元素为a[],第二个元素为b[],求最少的划分数使得每一种划分中a[]和b[]不是下降序列。

 

分析:这个问题是二元组的最少链划分,那么我们以a[]为关键字大小进行排序,如果a[]中相同就按照b[]排序,根据

Dilworth定理,然后题目就变成了求b[]序列中最长严格下降子序列长度了。


Source Code
Problem: 1065		User: fisty
Memory: 620K		Time: 0MS
Language: G++		Result: Accepted

    Source Code

    #include <cstdio>
    #include <algorithm>
    #include <vector>

    using namespace std;
    #define MAX_N 10010
    const int INF = 100000000;
    typedef pair<int , int> PII;
    int t,n;


    void  solve(int n, vector<PII>& sticks){
            sort(sticks.begin(), sticks.end());
            vector<int> dp(n, INF);

            for(int i = 0;i < n; i++){
                    int num =  -sticks[i].second;
     
                    *lower_bound(dp.begin(), dp.end(), num) = num;
            }

            printf("%d\n", lower_bound(dp.begin(), dp.end(), INF)-dp.begin());
    }
    int main(){
            scanf("%d", &t);
            while(t--){
                    scanf("%d", &n);
                    vector<PII> sticks(n);
                    for(int i = 0;i < n; i++){
                            scanf("%d %d", &sticks[i].first, &sticks[i].second);
                    }
                       
                    solve(n, sticks);
            }
            return 0;
    }
参考:http://blog.csdn.net/acdreamers/article/details/7626671

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值