洛谷1020 dilworth 定理 及最长上升,不上升子列,贪心

本文讲解了如何通过Dilworth定理分析导弹拦截系统的特性,找出最大不上升序列和最长上升序列,进而确定拦截导弹数量及所需系统数量。博客深入探讨了偏序集的分解原理,并提供了二分法和数学归纳法的解决方案。
摘要由CSDN通过智能技术生成

题目描述

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

输入导弹依次飞来的高度(雷达给出的高度数据是\le 50000≤50000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入格式

11行,若干个整数(个数\le 100000≤100000)

输出格式

22行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入输出样例

输入 #1复制

389 207 155 300 299 170 158 65

输出 #1复制

6
2

说明/提示

为了让大家更好地测试n方算法,本题开启spj,n方100分,nlogn200分

每点两问,按问给分

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

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

所以本题我们只要求解最大不上升数列以及最大上升数列即可,结合二分法,可以求出,

下面证明可以看 Dilworth定理,链还是反链? - 枕边梦 - 博客园

也可以看数学归纳法证明:

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

证明施归纳于n。

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

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

这个定理称为狄尔沃斯(Dilworth)定理或偏序集的分解定理,这是组合学三大存在性定理之一,有广泛的应用。这种分解是所有分解方法中反链个数最少的一种分解方法,因为A不可能分解成n-1条反链。假若只有n-1条反链,那么最长链的n个元素中必有2个元素被分到同一个反链,显然这与反链的定义矛盾。

注意:lower_bound ,upper_bound 使用可能成为坑

lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

#include<bits/stdc++.h>
using namespace std;
const int maxn=0x3f3f3f3f;
int a[100005],d[100005];
int len,l,r,pos,i,j,n,x;
void lis(){
    len=0;
    d[0]=maxn;
    for(i=1;i<=n;i++){
        if(a[i]<=d[len]){
            d[++len]=a[i];
            continue;
        }
        int l = 1, r = len, pos;
        while(l <= r) {
            int mid = (l + r) >> 1;
            if(d[mid] < a[i]) {
                pos = mid;
                r = mid - 1;
            } else l = mid + 1;
        }
        d[pos]=a[i];
    }
    printf("%d\n",len);
}    //求最长不上升序列长度
void solve(){
    len=0;
    d[0]=0;
    for(i=1;i<=n;i++){
        if(a[i]>d[len]){
            d[++len]=a[i];
            continue;
        }
        pos=lower_bound(d+1,d+1+len,a[i])-d;
        d[pos]=a[i];
    }
    printf("%d",len);
} //求最长上升序列
int main(){
    n=0;
    while(cin >> x) {
        if(x == 0) break;
        a[++n] = x;
    }
    lis();
    solve();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值