zoukankan      html  css  js  c++  java
  • Dilworth定理的应用和最长??序列

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

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

    简而言之,这个题目就是要求出最长的不递增序列和不递增序列的个数。
    Dilworth定理 (???什么东西) ,不递增序列的个数等于递增序列的最大长度
    所以我们只需要求出不递增序列的最大长度和递增序列的最大长度。(问题是这我也不会啊

    那么我们就要学习一下怎么求最大??序列的长度。

    1.O((n^2))

    第一问很明显是求最长不上升子序列,利用dp的思想,我们设f[i]为以第i个数为开头的最长不上升子序列的长度,然后可以得到这样一段程序

    for(int i=n;i>=1;i--){//注意!!因为它是以i开头的最长不上升子序列,所以这里要从n开始循环,不然会出现一些奇妙的bug 
            f[i]=1;//以第i个数为开头的最长不上升子序列的长度至少为1(当这个序列只有它本身这一个数) 
            for(int j=i+1;j<=n;j++){//用前面已经算好的来算现在正在算的这一个 
                if(a[j]<=a[i])//如果a[j]>a[i]的话就不满足不上升这个要求(毕竟a[j]在a[i]后面) 
                    f[i]=max(f[i],f[j]+1); //更新f[i]~记得要+1啊 
            }
            ans1=max(ans1,f[i]);//更新ans1 
        }
    

    2.小于O((n^2))

    在朴素n^2算法中,用f[i]储存以i结尾的最长不上升子序列长度,如样例

    i 1 2 3 4 5 6 7 8

    a 389 207 155 300 299 170 158 65

    f 1 2 3 2 3 4 5 6

    发现当f的值相同时,越后面的导弹高度越高

    用d[i]维护f值为i的最后一个导弹的位置,t记录当前已经求出最长不升子序列长度

    递推求f时枚举a[d[t]],a[d[t-1]],。。。,a[d[1]]是否≥当前求的导弹高度,是就更新f

    while(~scanf("%d",&a[++n]));//读入数据方法
        n--;//n是导弹数,由于某些原因要--
        for(int i=1; i<=n; i++) {
            f[i]=1;
            for(int j=t; j>0; j--)
                if(a[i]<=a[d[j]])
                {
                    f[i]=f[d[j]]+1;
                    break;
                }
            t=max(t,f[i]);
            d[f[i]]=i;//简单的维护过程
            ans=max(ans,f[i]);
        }
    

    3.O(nlogn)求出最长不上升子序列的长度

    (即一套系统最多拦截数)(终于到二了)

    1.实现方式
    首先我们需要一个数组a,存储从第1个到第n个导弹的高度

    然后一个数组d(其实是个栈),存储不上升序列

    把a中的每个元素挨个加到d里面:

    (a中第i个元素为a[i],d长度为len,d中最后一个(也是最小的一个)为d[len])

    如果a[i] <= d[len],说明a[i]可以接在d后面(而整个d还是有序的),那就简单粗暴地把a[i]丟进d:

    d[ ++len ] = a[i]
    

    如果a[i] > d[len],说明a[i]接不上
    但是我们发扬瞎搞精神:接的上要接,接不上创造条件也要接!

    强行把a[i]塞进去:
    在d中找到第一个小于a[i]的数,把它踹了,用a[i]代替它!(为什么正确在下面)

    2.为什么正确
    显然成立

    如果y在末尾,由于y < a[i],所以y后面能接的不如a[i]多,y让位给a[i]可以让序列更长

    如果y不在末尾,那y有生之年都不会再被用到了,直接踹了y就行,y咋样,who care?

    4.最快做法

    3方法增加二分 查找

    参考:导弹拦截 题解
    P3902 递增

    5、dp做法

    #include<bits/stdc++.h>
    #define intn long long
    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<string.h>
    #include<cmath>
    #include<stack>
    #define _0for(i, a) for(int i = 0; i < (a); ++i)
    #define _1for(i, a) for(int i = 1; i <=(a); ++i)
    #define lowbit(x) ((x)&(-x))
    #define debug(x) 
    (void)(cerr << "L" << __LINE__
    			<< " : " << #x << " = " 
    			<< (x) << endl )
    using namespace std;
    int dp1[200];
    int dp2[200];
    main(void)
    {
    	//前边要求单调递增dp1  
    	//后边要求单调递减dp2
    	//dp求最长升dp[i]表示以i为结尾的升序列的长度 
    	int n;
    	cin>>n;
    
    	int ans=0;
    	int a[200];
    	a[0]=0;
    	for(int i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	a[n+1]=0;
    	for(int i=1;i<=n;i++)//当前状态 
    	{
    		for(int j=0;j<i;j++)//上一个状态 
    		{
    			if(a[j]<a[i])
    			dp1[i]=max(dp1[i],dp1[j]+1); 
    		} 
    	}
    	for(int i=n;i>=1;i--)//当前状态 
    	{
    		for(int j=n+1;j>i;j--)//上一个状态 
    		{
    			if(a[j]<a[i])
    			dp2[i]=max(dp2[i],dp2[j]+1);
    		} 
    	}
    	for(int i=1;i<=n;i++)
    	{
    	//	debug(dp1[i]);
    	//	debug(dp2[i]);
    		ans=max(ans,dp1[i]+dp2[i]-1);
    	}
    	cout<<n-ans;
    }
    
    
    
    
    
  • 相关阅读:
    字典的key都可以是什么
    groupby 的妙用(注意size和count)

    希尔排序
    TCP和UDP
    闭包(python)
    快速排序
    mysql t4模板_Model
    vue前端性能优化
    系统稳定性问题总结
  • 原文地址:https://www.cnblogs.com/wangqianyv/p/13357759.html
Copyright © 2011-2022 走看看