sicily1060 poj3903
最长上升子序列
先dp,O(n^2)太慢了,超时。10000的规模需要1千万,不可接受,5000000可以接受。
View Code
1 #include <stdio.h> 2 3 int signal[40001]; 4 int length[40001]; 5 int main() 6 { 7 int test,p; 8 scanf("%d",&test); 9 for(int t=1;t<=test;t++){ 10 scanf("%d",&p); 11 for(int i=1;i<=p;i++) 12 scanf("%d",&signal[i]); 13 14 for(int i=1;i<=p;i++){ 15 int temp=0; 16 for(int j=1;j<=i-1;j++) 17 if(signal[j]<signal[i] && temp<length[j]) 18 temp=length[j]; 19 length[i]=1+temp; 20 } 21 int max=0; 22 for(int i=1;i<=p;i++) 23 if(length[i]>max) 24 max=length[i]; 25 printf("%d\n",max); 26 } 27 }
先回顾经典的O(n^2)的动态规划算法,设A[t]表示序列中的第t个数,F[t]表示从1到t这一段中以t结尾的最长上升子序列的长度,初始时设F[t] = 0(t = 1, 2, ..., len(A))。则有动态规划方程:F[t] = max{1, F[j] + 1} (j = 1, 2, ..., t - 1, 且A[j] < A[t])。
根据F[]的值进行分类。对于F[]的每一个取值k,我们只需要保留满足F[t] = k的所有A[t]中的最小值。设D[k]记录这个值,即D[k] = min{A[t]} (F[t] = k)。
注意到D[]的两个特点:
(1) D[k]的值是在整个计算过程中是单调不上升的。
(2) D[]的值是有序的,即D[1] < D[2] < D[3] < ... < D[n]。
利用D[],我们可以得到另外一种计算最长上升子序列长度的方法。设当前已经求出的最长上升子序列长度为len。先判断A[t]与D[len]。若A[t] > D[len],则将A[t]接在D[len]后将得到一个更长的上升子序列,len = len + 1, D[len] = A[t];否则,在D[1]..D[len]中,找到最大的j,满足D[j] < A[t]。令k = j + 1,则有D[j] < A[t] <= D[k],将A[t]接在D[j]后将得到一个更长的上升子序列,同时更新D[k] = A[t]。最后,len即为所要求的最长上升子序列的长度。
代码如下:
View Code
1 #include <stdio.h> 2 3 int signal[40001]; 4 int bkt[40001]; 5 6 int find2(int left,int right,int x) 7 { 8 for(int i=right;i>=left;i--){ 9 if(bkt[i]<x) 10 return i; 11 } 12 } 13 int main() 14 { 15 int test,p; 16 scanf("%d",&test); 17 for(int t=1;t<=test;t++){ 18 scanf("%d",&p); 19 for(int i=1;i<=p;i++) 20 scanf("%d",&signal[i]); 21 22 int len=1; 23 bkt[1]=signal[1]; 24 for(int i=2;i<=p;i++){ 25 if(bkt[len]<signal[i]){ 26 len++; 27 bkt[len]=signal[i]; 28 } 29 else{ 30 int k=find2(1,len-1,signal[i]); 31 if(bkt[k+1]>signal[i]) 32 bkt[k+1]=signal[i]; 33 } 34 } 35 printf("%d\n",len); 36 } 37 return 0; 38 }
在查找时可以用二分查找,速度更快
View Code
1 #include <iostream> 2 using namespace std; 3 #define MAX 100000 4 long stock[MAX+1]; 5 long bkt[MAX+1]; 6 7 int main() 8 { 9 int n; 10 int i,k; 11 while(cin>>n) 12 { 13 for(i=1;i<=n;i++) 14 cin>>stock[i]; 15 int len=1; 16 bkt[1]=stock[1]; 17 for(i=2;i<=n;i++){ 18 if(bkt[len]<stock[i]){ 19 len++; 20 bkt[len]=stock[i]; 21 } 22 else{ 23 //k=find2(1,len-1,stock[i]); //find argmax{bkt[k]<stock[i]} 24 int left=1,right=len-1; 25 int mid; 26 while(left<=right){ 27 mid=(left+right)/2; 28 if(stock[i]>bkt[mid]) 29 left=mid+1; 30 else 31 right=mid-1; 32 } 33 if(bkt[left]>stock[i]) //left is the one next to the largest one smaller than stock[i] 34 bkt[left]=stock[i]; 35 36 } 37 } 38 cout<<len<<endl; 39 } 40 return 0; 41 }


浙公网安备 33010602011771号