洛谷P1020,导弹拦截(LIS)

题目地址
考点:LIS
做法:时间复杂度O(nn)方法两种属于DP方法,时间复杂度O(nlog2(n))方法1种辅助数组方法,可结合分治理解。
分析:问题在于使用几套拦截设备,也就是说最少多少个严格递减的子序列。
1.假设我们有两个单调递减的子序列,那么其中一个必然能拿出一个数可以比另一个序列中至少一个数要大,否则我拿出的这个数完全可以到另一个序列里,也就不满足最少单调递减子序列
2.所以每一个单调递减子序列必然能拿出一个数且这些数可组成一个单调递增的序列,而且这个单调递增序列的长度是LIS
这两段不是AC代码!

#include <bits/stdc++.h>
using namespace std;
int dp[100010],con,high[100010];
int LIS()
{
     dp[1]=1;
     int ans=0;
     for(int i=2;i<=con;i++)
     {
         for(int j=1;j<i;j++)
         {
             if(high[i]>high[j])
                 dp[i]=max(0,dp[j])+1;
             else
                dp[i]=1;
         }
         ans=max(ans,dp[i]);
     }
    return ans;
}

int main()
{
    con=0;
    int n;
    while(cin>>n&&n)
    {
        con++;
        high[con]=n;
    }
    cout<<LIS()<<endl;
    return 0;
}


书上的代码是这样的,我不是很理解,但是书上的推导过程和我的是一样的就是 dp【i】代表以i结尾的递增子序列长度,如果a[i]>a[j](j<i)那么dp[i]=dp[j]+1,否则dp[i]=1;

#include <bits/stdc++.h>
using namespace std;
int dp[100010],con,high[100010];
int LIS()
{
    dp[1]=1;
    int ans=1,max1=0;
    for(int i=2;i<=con;i++)
    {
        max1=0;
        for(int j=1;j<i;j++)
        {
            if(dp[j]>max1&&high[i]>high[j])
               { max1=dp[j];}
            dp[i]=max1+1;
            if(dp[i]>ans)
                ans=dp[i];
        }
    }
    return ans;
}
int main()
{
    con=0;
    int n;
    while(cin>>n&&n)
    {
        con++;
        high[con]=n;
    }
    cout<<LIS()<<endl;
    return 0;
}

这是用DP方法解决了这个题的LIS问题,如果不知道这个规律说实在的真想不到怎么做,然后最是再写个函数求一下LDS(最长递减子序列)分行输出就行。

我们看一下运用了辅助数组和二分查找原理的log2(n*n)复杂度方法

原理:采用一个辅助数组,原数组第一项直接存入其中,之后开始遍历原数组的每一项,假如发现了大于目前辅助数组中最后一项的数直接加在尾部,如果发现了小于最后一项的数,我们就要用a[i]替换在辅助数组中第一个大于a[i]的数

注意:这个方法只能用于求LIS的长度,并不代表能求出准确的LIS,比如 4 8 9 5 6 7
按照这个算法最终输出4 5 6 7是没错的
但是如果是 4 8 9 5 6 1
算法最终输出 1 5 6显然不对!

下面附上代码

#include <iostream>
using namespace std;
int main()
{
    int con=0,n,a[1000];
    while(cin>>n&&n)
    {
        a[con]=n;
         con++;
    }
    int LIS[1000];
    LIS[0]=a[0];
    int len=0;
    for(int i=1;i<con;i++)
    {
        if(a[i]>LIS[len])
        {
            LIS[++len]=a[i];
        }
        else
        {
            int p=lower_bound(LIS,LIS+len,a[i])-LIS;
            LIS[p]=a[i];
        }
    }
    cout<<len+1<<endl;
    return 0;
}

额外的:
另外就是另一个DP算法,这个算法借助了LCS的操作

也是我最不推荐的一种,因为其本神时间和空间复杂度非常大,并不能体现DP的优越性,只是说能解决问题罢了。

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int con=0,n,ans=0,a[1000],b[1000],dp[100][100]={0};
    while(cin>>n&&n)
    {
        a[con]=n;
        b[con]=a[con];
         con++;
    }
     sort(b,b+con);
     for(int i=1;i<=con;i++)
     {
         for(int j=1;j<=con;j++)
         {
             if(a[i-1]==b[j-1])
             {
                 dp[i][j]=dp[i-1][j-1]+1;
             }
             else
             {
                 dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
             }
            if(dp[i][j]>ans)
            {
             ans=dp[i][j];
            }
         }
     }
     cout<<ans<<endl;
    return 0;
}
上一篇:【PTA】归并排序 7-4 求逆序对数目 (20 分)


下一篇:1. 两数之和