LIS算法:经典DP问题( 导弹拦截问题 )

        题目简单描述:为了拦截敌国的袭击,科学家研发出一套导弹系统,导弹系统有个缺陷:第一发炮弹可以到达任意高度,然而之后的每一发炮弹都不能高于前一发的高度。

        现给出数个导弹的高度( <=50000的正整数 ),计算一套导弹拦截系统最多可以拦截多少导弹,如果需要拦截全部导弹需要多少套导弹拦截系统?

        LIS模板题,第一问是求一个数列的最长下降子序列,第二问则是最长的上升子序列,因为最长上升子序列的每一个高度都需要一套拦截系统,彼此独立。

        将求全部数列最长上升子序列问题分解成求前n个子数列的最长上升子序列,初始化存储数组dp各长度为1,因为可能用到i的历史数据是比当前长度短的数列,所以对每个长度从低到高计算。

        状态转移方程:dp[ind] = Max( dp[ind],dp[jnd]+1 ),a[jnd]>=a[ind],1<=jnd

        也就是说如果在自己之前的数字( a[jnd] )符合大小比较,就考虑是否选取dp[jnd]+1来更新dp[ind]。

#include
#include
#define Max(a,b) (a>b?a:b)
int a[100000+5];
int dp[100000+5];
int sp[100000+5];
void memsets(int* array,int cnt)
{
    for(int i=1;i<=cnt;i++)
        array[i]=1;
    return;
}
int main(int argc, char** argv)
{
    int i=1;
    while(scanf("%d",&a[i++])!=EOF);
    i-=2;
    memsets(dp,i),memsets(sp,i);
    int ans=0,anss=0;
    for(int ind=2;ind<=i;ind++)
    {
        for(int jnd=1;jnd=a[ind])
                dp[ind]=Max(dp[jnd]+1,dp[ind]);
            if(a[jnd]

        上面就是时间复杂度是O(n^2)的基本求法,这样的速度太慢了,如何进一步优化?考虑能不能减少 jnd 这一层循环的冗余,需要求出最长上升序列,那么只需要不断的维护当前最优的上升子序列即可,不断的用数据更新,最终得到的就是最长也是最佳的上升子序列。

        从数列头部开始,如果当前数大于现有上升子序列最后一个值,那么直接把它添加到上升子序列的末尾;但如果它小于最后一个数值,那么就从现有上升子序列的头部开始判断,代替掉第一个比它大的值,这样维护的上升子序列会在保证最长的前提下,保持每个数都是最小,以方便后来的数更大可能进序列!

        如 1 2 5 3 4 6

        第一次:1

        第二次:1 2

        第三次:1 2 5

        第四次:1 2 3

        第五次:1 2 3 4

        第六次:1 2 3 4 6

#include
#include
#define write printf("%d\n",cnt)
#define initialize cnt=1,dp[1]=a[1]
#define Max(a,b) (a>b?a:b)
int a[100000+5];
int dp[100000+5];
int main(int argc, char** argv)
{
    int i=1;
    while(scanf("%d",&a[i++])!=EOF);
    i-=2;
    int ans=0,nss=0;
    int cnt;
    initialize;
    for(int ind=2;ind<=i;ind++)
    {
        if(dp[cnt]>=a[ind])
            dp[++cnt]=a[ind];
        else
            for(int jnd=1;;jnd++)
            {
                if(dp[jnd]=a[ind])
                {
                    dp[jnd]=a[ind];
                    break;
                }
            }
    }
    write;
    return 0;
}

        *二分法优化:每次不满足放在列尾时,都需要从头到尾依次寻找,不如加一个二分查找又能省去近半的时间。二分的判断条件需要格外注意,第一问的最长不升子序列( 可以下降或是保持不变 ),所以判断条件应该是dp[fen]>=a[ind],满足则左边界left = fen+1,不满足就是右边界right = fen。第二问同理,把握是否应该替换相同项即可!

#include
#include
#define write printf("%d\n",cnt)
#define initialize cnt=1,dp[1]=a[1]
#define Max(a,b) (a>b?a:b)
int a[100000+5];
int dp[100000+5];
int main(int argc, char** argv)
{
    int i=1;
    while(scanf("%d",&a[i++])!=EOF);
    i-=2;
    int ans=0,nss=0;
    int cnt;
    initialize;
    for(int ind=2;ind<=i;ind++)
    {
        if(dp[cnt]>=a[ind])
            dp[++cnt]=a[ind];
        else
        {
            int left=1,right=cnt;
            while(left=a[ind])
                    left=fen+1;
                else
                    right=fen;
            }
            dp[left]=a[ind];
        }
    }
    write;
    initialize;
    for(int ind=2;ind<=i;ind++)
    {
        if(dp[cnt]

 

你可能感兴趣的:(DP)