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 }

 

posted @ 2012-10-21 20:59  sidereal  Views(137)  Comments(0)    收藏  举报