【动态规划】【Dilworth定理】AOJ 0033 Ball #挑战程序设计竞赛

本文探讨了一种使用Dilworth定理解决序列划分问题的方法,通过求解最长下降子序列来判断序列是否能划分为两个链。介绍了Dilworth定理的基本概念及其在偏序集中的应用,提供了动态规划求解最长下降子序列的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

有n个序列,每个序列长度都是10且每个序列中元素互不相同,简单记录为

x1,x2,...,x10 x_1,x_2,...,x_{10} x1,x2,...,x10

问每个序列可不可以划分这样两个集合,其中每个集合中任意两个元素都满足

xi<xj&&i<j x_i < x_j \quad \&\&\quad i < j xi<xj&&i<j

对每个序列,可以按照上述条件划分,则输出YES,否则输出NO。

每个集合中任意两个元素都是可比的,这自然让我们想到偏序集的概念,而给定的条件正是一个构成偏序集的条件。这个题在问我们原集合可不可以划分为两个链。链是一个集合,其中任意两个元素都可比。而划分的意思是者两个链相交为空,相并为原集。

那我们如何判断这个集合可不可以划分为2个链呢?这时就要请出我们的Dilworth定理了。它是解决偏序集中链与反链极值问题的利器。反链也是一个集合,它的定义是,其中的任意两个元素都不可比。Dilworth定理的表述如下:

对偏序集(X,≤\le),如果

  • 可找到的最长链的长度为rrr,那么这个集合可以划分出rrr条反链且不能再少。
  • 可找到的最长反链的长度为lll, 那么这个集合可以划分出lll条链且不能再少。

所以要判断原集合如果拥有一条长度大于2的反链,它就必定不能被划分为2条链。那么我们如何知道有没有长度大于2的反链呢?只需要看看它最长的那个反链。这是我的思考顺序,可能有些别扭哈。

观察反链的结构,把它们按照脚标顺序由小到大排列,为了不可比,脚标小的值就大,故我们要求的东西就很清楚了,就是原序列的最长下降子序列。注意读题时要认真,要抓住序列中两两不同的特点观察集合的特征。

求最长下降子序列就没什么好说的了,动态规划求就好了。不再赘述。

另外,在《挑战程序设计竞赛》中,这道题应该是用深搜做的,因为只有10个数,剪剪枝就差不多了。

源码如下:

//Dilworth 
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n, max_len;
int num[11], dp[11];

int main(){
    //freopen("in.txt", "r", stdin);
    scanf("%d", &n);
    while(n--){
        for(int i=0; i<10; i++){
            scanf("%d", &num[i]);
        }
        memset(dp, 0, sizeof(dp));
        max_len = 1;
        dp[0] = 1;
        
        for(int i=1; i<10; i++){
            for(int j=0; j<i; j++){
                if(num[i] < num[j])
                    dp[i] = max(dp[i], dp[j] + 1);
            }
            max_len = max(max_len, dp[i]);
        }

        if(max_len > 2)
            printf("NO\n");
        else 
            printf("YES\n");
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值