<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 195, comments - 34, trackbacks - 0, articles - 1

    求數(shù)組中最長遞增子序列

    Posted on 2009-10-25 11:27 小強(qiáng)摩羯座 閱讀(2923) 評論(1)  編輯  收藏 所屬分類: 算法編程
    最長遞增子序列的求法 LIS (轉(zhuǎn))

    什么是最長遞增子序列呢?
    問題描述如下:
       設(shè)L=<a1,a2,…,an>是n個不同的實數(shù)的序列,L的遞增子序列是這樣一個子序列Lin=<aK1,ak2,…,akm>,其中k1<k2<…<km且aK1<ak2<…<akm。求最大的m值。
    對于這個問題有以下幾種解決思路:
       1、把a(bǔ)1,a2,...,an排序,假設(shè)得到a'1,a'2,...,a'n,然后求a的a'的最長公共子串,這樣總的時間復(fù)雜度為o(nlg(n))+o(n^2)=o(n^2);
       2、動態(tài)規(guī)劃的思路:
        另設(shè)一輔助數(shù)組b,定義b[n]表示以a[n]結(jié)尾的最長遞增子序列的長度,則狀態(tài)轉(zhuǎn)移方程如下:b[k]=max(max(b[j]|a[j]<a[k],j<k)+1,1);
        這個狀態(tài)轉(zhuǎn)移方程解釋如下:在a[k]前面找到滿足a[j]<a[k]的最大b[j],然后把a(bǔ)[k]接在它的后面,可得到a[k]的最長遞增子序列的長度,或者a[k]前面沒有比它小的a[j],那么這時a[k]自成一序列,長度為1.最后整個數(shù)列的最長遞增子序列即為max(b[k]   | 0<=k<=n-1);
        實現(xiàn)代碼如下:
        

    #include <iostream>

    using namespace std;

    int main()

    {

           int i,j,n,a[100],b[100],max;

           while(cin>>n)

           {

                  for(i=0;i<n;i++)

                         cin>>a[i];

                  b[0]=1;//初始化,以a[0]結(jié)尾的最長遞增子序列長度為1

                  for(i=1;i<n;i++)

                  {

                         b[i]=1;//b[i]最小值為1

                         for(j=0;j<i;j++)

                                if(a[i]>a[j]&&b[j]+1>b[i])

                                       b[i]=b[j]+1;

                  }

                  for(max=i=0;i<n;i++)//求出整個數(shù)列的最長遞增子序列的長度

                         if(b[i]>max)

                                max=b[i];

                  cout<<max<<endl;

           }

           return 0;

    }

        顯然,這種方法的時間復(fù)雜度仍為o(n^2);
       3、對第二種思路的改進(jìn):
        第二種思路在狀態(tài)轉(zhuǎn)移時的復(fù)雜度為o(n),即在找a[k]前面滿足a[j]<a[k]的最大b[j]時采用的是順序查找的方法,復(fù)雜度為o(n).
        設(shè)想如果能把順序查找改為折半查找,則狀態(tài)轉(zhuǎn)移時的復(fù)雜度為o(lg(n)),這個問題的總的復(fù)雜度就可以降到nlg(n).
        另定義一數(shù)組c,c中元素滿足c[b[k]]=a[k],解釋一下,即當(dāng)遞增子序列的長度為b[k]時子序列的末尾元素為c[b[k]]=a[k].
        先給出這種思路的代碼,然后再對其做出解釋。
        

    #include <iostream>

    using namespace std;

    int find(int *a,int len,int n)//若返回值為x,a[x]>=n>a[x-1]

    {

           int left=0,right=len,mid=(left+right)/2;

           while(left<=right)

           {

                  if(n>a[mid]) left=mid+1;

                  else if(n<a[mid]) right=mid-1;

                  else return mid;

                  mid=(left+right)/2;

           }

           return left;

    }

    void fill(int *a,int n)

    {

           for(int i=0;i<=n;i++)

                  a[i]=1000;

    }

    int main()

    {

           int max,i,j,n,a[100],b[100],c[100];

           while(cin>>n)

           {

                  fill(c,n+1);

                  for(i=0;i<n;i++)

                         cin>>a[i];

                  c[0]=-1;//    …………………………………………1

                  c[1]=a[0];//        ……………………………………2

                  b[0]=1;//     …………………………………………3

                  for(i=1;i<n;i++)//        ………………………………4

                  {

                         j=find(c,n+1,a[i]);//   ……………………5

                         c[j]=a[i];// ………………………………6

                         b[i]=j;//……………………………………7

                  }

                  for(max=i=0;i<n;i++)//………………………………8

                         if(b[i]>max)

                                max=b[i];

                  cout<<max<<endl;

           }

           return 0;

    }

        對于這段程序,我們可以用算法導(dǎo)論上的loop invariants來幫助理解.
        loop invariant: 1、每次循環(huán)結(jié)束后c都是單調(diào)遞增的。(這一性質(zhì)決定了可以用二分查找)
                               2、每次循環(huán)后,c[i]總是保存長度為i的遞增子序列的最末的元素,若長度為i的遞增子序

                                      列有多個,剛保存末尾元素最小的那個.(這一性質(zhì)決定是第3條性質(zhì)成立的前提)
                               3、每次循環(huán)完后,b[i]總是保存以a[i]結(jié)尾的最長遞增子序列。
        initialization:    1、進(jìn)入循環(huán)之前,c[0]=-1,c[1]=a[0],c的其他元素均為1000,c是單調(diào)遞增的;
                               2、進(jìn)入循環(huán)之前,c[1]=a[0],保存了長度為1時的遞增序列的最末的元素,且此時長度為1

                                     的遞增了序列只有一個,c[1]也是最小的;
                               3、進(jìn)入循環(huán)之前,b[0]=1,此時以a[0]結(jié)尾的最長遞增子序列的長度為1.
        maintenance:   1、若在第n次循環(huán)之前c是單調(diào)遞增的,則第n次循環(huán)時,c的值只在第6行發(fā)生變化,而由

                                    c進(jìn)入循環(huán)前單調(diào)遞增及find函數(shù)的性質(zhì)可知(見find的注釋),

                                     此時c[j+1]>c[j]>=a[i]>c[j-1],所以把c[j]的值更新為a[i]后,c[j+1]>c[j]>c[j-1]的性質(zhì)仍然成

                                    立,即c仍然是單調(diào)遞增的;
                               2、循環(huán)中,c的值只在第6行發(fā)生變化,由c[j]>=a[i]可知,c[j]更新為a[i]后,c[j]的值只會變

                                      小不會變大,因為進(jìn)入循環(huán)前c[j]的值是最小的,則循環(huán)中把c[j]更新為更小的a[i],當(dāng)

                                     然此時c[j]的值仍是最小的;
                               3、循環(huán)中,b[i]的值在第7行發(fā)生了變化,因為有l(wèi)oop invariant的性質(zhì)2,find函數(shù)返回值

                                    為j有:c[j-1]<a[i]<=c[j],這說明c[j-1]是小于a[i]的,且以c[j-1]結(jié)尾的遞增子序列有最大的

                                   長度,即為j-1,把a(bǔ)[i]接在c[j-1]后可得到以a[i]結(jié)尾的最長遞增子序列,長度為(j-1)+1=j;
        termination:       循環(huán)完后,i=n-1,b[0],b[1],...,b[n-1]的值均已求出,即以a[0],a[1],...,a[n-1]結(jié)尾的最長遞

                                  增子序列的長度均已求出,再通過第8行的循環(huán),即求出了整個數(shù)組的最長遞增子序列。

              仔細(xì)分析上面的代碼可以發(fā)現(xiàn),每次循環(huán)結(jié)束后,假設(shè)已經(jīng)求出c[1],c[2],c[3],...,c[len]的值,則此時最長遞增子序列的長度為len,因此可以把上面的代碼更加簡化,即可以不需要數(shù)組b來輔助存儲,第8行的循環(huán)也可以省略。
        

    #include <iostream>

    using namespace std;

    int find(int *a,int len,int n)//修改后的二分查找,若返回值為x,則a[x]>=n

    {

           int left=0,right=len,mid=(left+right)/2;

           while(left<=right)

           {

                  if(n>a[mid]) left=mid+1;

                  else if(n<a[mid]) right=mid-1;

                  else return mid;

                  mid=(left+right)/2;

           }

           return left;

    }

    int main()

    {

           int n,a[100],b[100],c[100],i,j,len;//新開一變量len,用來儲存每次循環(huán)結(jié)束后c中已經(jīng)求出值的元素的最大下標(biāo)

           while(cin>>n)

           {

                  for(i=0;i<n;i++)

                         cin>>a[i];

                  b[0]=1;

                  c[0]=-1;

                  c[1]=a[0];

                  len=1;//此時只有c[1]求出來,最長遞增子序列的長度為1.

                  for(i=1;i<n;i++)

                  {

                         j=find(c,len,a[i]);

                         c[j]=a[i];

                         if(j>len)//要更新len,另外補(bǔ)充一點(diǎn):由二分查找可知j只可能比len1

                                len=j;//更新len

                  }

                  cout<<len<<endl;

           }

           return 0;

    }

    最長遞增部分序列 Longest Ordered Subsequence Extention hoj10027 poj2533
    2007-08-21 20:19

    求最長遞增部分序列是一個比較常見的動態(tài)規(guī)劃題。在導(dǎo)彈攔截等題中都有用到。一般來說就是用經(jīng)典的O(n^2)的動態(tài)規(guī)劃算法。

    算法如下:

             設(shè)A[i]表示序列中的第i個數(shù),F(xiàn)[i]表示從1到i這一段中以i結(jié)尾的最長上升子序列的長度,初始時設(shè)F[i] = 0(i = 1, 2, ..., len(A))。則有動態(tài)規(guī)劃方程:F[i] = max{1, F[j] + 1} (j = 1, 2, ..., i - 1, 且A[j] < A[i])。

    然而在hoj10027中n的值達(dá)到了50000。顯而易見經(jīng)典算法是會超時滴。所以只有另謀出路了。

             用一個變量len記錄到目前為止所找出來的最長遞增序列的長度。另外準(zhǔn)備一個數(shù)組b[],用這個數(shù)組表示長度為j的遞增序列中最后一個元素的值。在這里長度為j的遞增序列不止一個,我們所要保存是那個最小的。為什么呢?因為最后一個元素越小,那么這個遞增序列在往后被延長的機(jī)會越大。初始化b[0] = -1;len = 0;從第一個元素a[1]開始       a[i]( 1 <= i <= n)。如果這個元素比len長的序列的最大值大。則把這個元素直接添加到b數(shù)組的后面。如果這個元素比b數(shù)組的第一個元素還要小則把這個元素賦給b數(shù)組的第一個值。否則進(jìn)行二分查找。當(dāng)在b數(shù)組里面找到一個數(shù)比a[i]小,并且他的后面的數(shù)大于或等于a[i]則跳出。將a[i]添加到這個數(shù)的后面。輸出len就可以了。

    代碼如下:

    #include <stdio.h>
    #include <string.h>
    int main()
    {
    int a[50001], b[50001];
    int i, j, l, r, len, n, mid;
    while (scanf("%d", &n) != EOF)
    {
       for (i = 0; i < n; i++)
        scanf("%d", &a[i]);
       len = 0;
       memset(b, 0, sizeof(int) * 50001);
       b[0] = -1;
       for (i = 0; i < n; i++)
       {
        if (a[i] > b[len])
         b[++len] = a[i];
        else if (a[i] < b[1] )
         b[1] = a[i];
        else
        {
         l = 1; r = len;
         while (l <= r)
         {
          mid = (l + r)>>1;
          if (a[i] > b[mid] && a[i] <= b[mid + 1])
          {
           j = mid;
           break;
          }
          if (b[mid] > a[i])
          {
           r = mid - 1;
          }
          else
          {
           j = mid;
           l = mid + 1;
          }
         }
         b[j + 1] = a[i];
        }
       }
       printf("%d\n", len);
    }
    return 0;



    Feedback

    # re: 求數(shù)組中最長遞增子序列  回復(fù)  更多評論   

    2012-02-09 11:58 by 琉璃囧
    這是原創(chuàng)麼?但是如果要輸出LIS的元素..2 3 7 6 8 4 5 9 1的輸出結(jié)果不正確吖~怎么改進(jìn)才可以得到正確的序列呢?
    主站蜘蛛池模板: 免费一级毛片在线播放| 亚洲爆乳精品无码一区二区| 国产91在线免费| 免费精品国产自产拍在线观看图片 | 亚洲国产精品无码专区在线观看| 免费看的成人yellow视频| 永久在线免费观看| 黄页免费在线观看| 一级有奶水毛片免费看| 亚洲AV女人18毛片水真多| 久久精品国产亚洲av麻豆图片| 亚洲av日韩综合一区在线观看| 国产亚洲精品看片在线观看| 免费一级毛片女人图片| 免费看美女被靠到爽的视频| 成人黄软件网18免费下载成人黄18免费视频 | 色偷偷亚洲第一综合网| 自拍偷区亚洲国内自拍| 亚洲国产成AV人天堂无码| 久久久久久亚洲Av无码精品专口 | 你懂的免费在线观看网站| 和老外3p爽粗大免费视频| 人妻无码中文字幕免费视频蜜桃| 亚洲a∨无码一区二区| 亚洲熟妇久久精品| 在线亚洲午夜片AV大片| 色偷偷女男人的天堂亚洲网| 亚洲av无码国产综合专区| 亚洲AV无码精品蜜桃| 国产成人精品日本亚洲网址| 亚洲免费观看在线视频| 亚洲人成在线中文字幕| 色偷偷亚洲女人天堂观看欧| 亚洲人成网站在线播放2019| 亚洲综合精品成人| 亚洲AV永久无码精品放毛片| 国产精品亚洲AV三区| 国产91成人精品亚洲精品| 又大又硬又粗又黄的视频免费看| eeuss影院www天堂免费| 中出五十路免费视频|