1. 程式人生 > >初入DP:最長上升子序列(LIS)

初入DP:最長上升子序列(LIS)

Description
我們都知道上體育課時,體育老師會讓我們按身高從小到大(或從大到小)排成一排。可是近日體育老師周老師卻有點煩心,
他教的班級來了幾個插班生,可他們有的不守規矩,沒有按照身高大小來插入隊伍,導致隊伍很難看。現在周老師有一個問
題:給定一排人的身高,問能否至多去掉一個人,使得隊伍裡的人的身高又變成從小到大(或從大到小)?

注意: 如果一排人的身高為 1 2 2 或 2 2 1是合法的。

輸入格式
第一行一個整數T,表示case數
接著是一個n,表示有n個人
接著是n個整數,第i個整數表示第i個人身高為a[i]
T <= 1000 , 2 <= n <= 1000 , a[i] <= 10000

輸出格式
如果可以,輸出”YES”,否則輸出”NO”。每個case佔一行。

輸入樣例
3
3
1 4 5
3
9 2 3
5
1 5 2 4 3

輸出樣例
YES
YES
NO
分析:題目意思很明確,就是找最長上升子序列或最長下降子序列的長度,看是否大於等於n-1,那麼最長上升子序列是個什麼東東呢?現在我們一起來看看吧!

LIS:
最長上升子序列涉及的演算法是DP,為什麼呢?因為我們求一個長度為n的LIS可以通過求長度為n-1的LIS,再加上一,而長度為n-1的LIS又可以由求長度為n-2的LIS加一得到,以此類推。符合把DP思想中的把問題分解為更小的子問題,而且全域性最優解由區域性最優解得到。

我們舉個栗子吧
2 7 1 5 6 4 3 8 9 用陣列num[i]來表示到陣列a[i]為止的最長上升子序列
一開始給num[i]全部賦值為1(因為最短的上升子序列為它自己本身)
為了方便,序列由1開始編號
1.a[1] = 2,num[1] = 1;
2.a[2] = 7,前面序列為2,2比7小,那麼num[2] = num[1] + 1=2;
3.a[3] = 1,前面序列為2,7,沒有比它小的,則num[3] = 1;
4.a[4] = 5,前面序列為2,7,1,前面比它小的是2,則num[4] = num[1] + 1=2;
5.a[5] = 6,前面序列為2,7,1,5,前面比它有2,5,則num[5] = num[4] + 1=3;
6.a[6] = 4,前面序列為2,7,1,5,6,前面比它小有2,則num[6] = num[1] + 1=2;
7.a[7] = 3,前面序列為2,7,1,5,6,4,前面比它小2,則num[7] = num[1] + 1;
8.a[8] = 8,前面序列為2,7,1,5,6,4,3,前面比它小有2,5,6,則num[8] = num[5] + 1 = 4;
9.a[9] = 9,前面序列為2,7,1,5,6,4,3,8,前面比它小有2,5,6,8,則num[9] = num[8] + 1= 5;
那麼最後一步就是在num[i]中找最大的值,即為序列的最長上升子序列的長度。
總結:就是在a[i]中找a[1]到a[i]中最長上升子序列,然後把到每個位置的最長上升子序列長度存起來,最後再比較各位置時的最長上升子序列長度,找到最大值即可。

#include <stdio.h>
int a[1005],b[1005];
int list1(int n)
{
    int num[1005],i,j,maxx;
    for(i=0; i<n; i++)
    {
        num[i] = 1;
        for(j=0; j<i; j++)
        {
            if((a[j] <= a[i]) && (num[j] + 1 >num[i]))
                num[i] = num[j] + 1;
        }
    }
    for(i=0; i<n; i++)
    {
        if(i==0 || num[i] > maxx)
            maxx = num[i];
    }
    return maxx;
}

int list2(int n)
{
    int num[1005],i,j,maxx;
    for(i=0; i<n; i++)
    {
        num[i] = 1;
        for(j=0; j<i; j++)
        {
            if((a[j] >= a[i]) && (num[j] + 1 >num[i]))
                num[i] = num[j] + 1;
        }
    }
    for(i=0; i<n; i++)
    {
        if(i==0 || num[i] > maxx)
            maxx = num[i];
    }
    return maxx;
}

int main()
{
   int kase,n,i,result1,result2;
   scanf("%d",&kase);
   while(kase--)
   {
       scanf("%d",&n);
       for(i=0; i<n; i++)
          scanf("%d",&a[i]);
       result1 = list1(n);
       result2 = list2(n);
       if(result1 >= n-1 || result2 >= n-1)
          printf("YES\n");
       else
          printf("NO\n");
   }
}