1. 程式人生 > >最長遞減子序列[轉載]

最長遞減子序列[轉載]

Q:例如:有一個序列,例如 9 8 2 1 7 5 3 4 3 2 1.
     求出最長的遞減子序列。如本例的結果就是:9 8 7 5 4 3 2 1。

分析:

        可採用動態規劃的思想進行解答,時間複雜度為O(n^2).

       設原陣列為a[1....n]。另設一陣列d[1....n],其中d[i]表示從第i個元素開始(一定包含第i個元素),到整個陣列末尾的子序列 a[i...n]中的最長遞減子序列的長度。
       則本問題就是要在求出d[1]的同時,恢復出最優解。

   下面給出遞推式:

d[i]的值分兩種情況:
1、當i=n時,d[i]=1。即最後一個元素的序列的最大遞減子序列中只有它自己。


2、當i<n時,d[i]=max{d[k]| i<k<=n 且a[i]>a[k]} +1。解釋意思為,包含第i個元素的序列a[i...n]的最大子序列依賴於i後面所有的序列中比a[i]小(滿足遞減
特性),且最大的d[k](滿足最 優特性)值再加1(加上a[i]元素)。在給d[i]賦值的時候只需記錄p[i]=k,既可以作為parent屬性恢復出解。

具體實現的話,開兩個陣列d[n],p[n],外層迴圈從後往前選取i,內層迴圈從i往後尋找最優的k,雙迴圈遍歷即可求出所有的d[i]。然後 再進行一次O(n)操作,找出最大的d[max]。恢復解的話,可以從p[max]開始,依次恢復出各個解。

複製程式碼
 1 #include <iostream.h>
 2 
 3 void longest_decrease_sub(int *a, int size)
 4 {
 5     
 6     int *d=new int[size]; //分配記憶體空間    
 7     int *p=new int[size];  //分配記憶體空間
 8     
 9     d[size-1]=1;
10     
11     for(int i=size-1;i>=0;i--)
12     {
13         
14         int max=0;
15         
16
int index=0; 17 18 for(int j=i;j<size;j++) 19 { 20 21 if(a[i]>a[j] && max <d[j]) 22 { 23 24 max=d[j]; 25 26 index=j; 27 28 } 29 30 } 31 32 if(max==0) 33 { 34 35 d[i]=1; 36 37 p[i]=-1; 38 39 } 40 else 41 { 42 43 d[i]=max+1; 44 45 p[i]=index; 46 47 } 48 49 } 50 51 //尋找最大子序列的起始下標 52 53 int max=0; 54 55 int max_index=0; 56 57 for( i=0;i<size;i++) 58 { 59 60 if(d[i]>max) 61 { 62 63 max=d[i]; 64 65 max_index=i; 66 67 } 68 69 } 70 71 //從最大子序列的下標開始 輸出子序列 72 cout<<"\n最長遞減子序列的長度為:"<<d[max_index]<<",最長子序列為:"<<ends; 73 74 for( i=max_index;i!=-1;i=p[i]) 75 { 76 77 cout<<a[i]<<" "<<ends; 78 79 } 80 81 delete [] d; 82 83 delete [] p; 84 85 } 86 87 void main() 88 { 89 int data[10]={1,2,5,4,3,2,7,8,9,0}; 90 longest_decrease_sub(data,10); 91 }
複製程式碼