pku1631 Bridging signals

http://poj.org/problem?id=1631

DP 最长上升子序列

最长上升子序列,因为n很大(n<40000),我开始写的O(n^2)的算法超时了。。。

学了一下O(n*logn)的方法,dp[i]表示当前状态下,所有长度为i的子序列中,末尾元素最小的那个子序列,的末尾元素值。

例如序列:a[6]:(1,3,5,4,6,2)

考虑到前6个元素的时候

长度为3的子序列有:(1,3,5)  (1,3,4)  (1,3,6)  (3,5,6)  (3,4,6)  那么dp[3] = 4;

长度为4的子序列有:(1,3,5,6)  (1,3,4,6)  那么dp[4] = 6;

开始时dp数组为空,依次遍历原序列a[n],对每一个当前元素a[i],都要更新一次dp数组

更新规则:从已有的dp数组中,找到第一个比当前元素a[i]大的元素,返回下标j, 然后更新 dp[j] = a;

到此,总体还是一个O(n*n)的算法,

但因为dp数组是单调的,可以用二分查找,使更新操作变成O(logn)

也可以从后向前查找dp数组,找到后用break;节省大概一半的时间,虽然是O(n^2),也可以A掉本题。。。

O(n*logn)代码:

 1 #include <stdio.h>
 2  
 3  int n, a[43210], dp[43210];
 4  const int maxint = (1<<31)-1;
 5  
 6  int bs(int l ,int r, int x)
 7  {
 8      int m;
 9      while(l < r)
10      {
11          m = (l + r)>>1;
12          if(dp[m] < x)
13          {
14              l = m + 1;
15          }
16          else
17          {
18              r = m;
19          }
20      }
21      return l;
22  }
23  
24  int LIS()
25  {
26      int i, j, maxn = 1;
27      dp[0] = 0;
28      dp[1] = maxint;
29      for(i=1; i<=n; i++)
30      {
31          j = bs(0, maxn+1, a[i]);
32          dp[j] = a[i];
33          if(j > maxn)
34          {
35              maxn ++;
36              dp[maxn+1] = maxint;
37          }
38      }
39      return maxn;
40  }
41  
42  int main()
43  {
44      int t, i;
45      scanf("%d", &t);
46      while(t-- && scanf("%d", &n))
47      {
48          for(i=1; i<=n && scanf("%d", a+i); i++);
49          printf("%d\n", LIS());
50      }
51      return 0;
52  }
原文地址:https://www.cnblogs.com/yuan1991/p/pku1631.html