Longest ascending subsequence && longest non-descending subsequence

Bailian 2757: Title description : For a given sequence, find the length of the longest ascending subsequence.

Topic link : http://bailian.openjudge.cn/practice/2757

Problem solving ideas

1. Dynamic programming 1. Finding subproblems

Error finding method:
"Finding the length of the longest ascending subsequence of the first n elements of a sequence" is a subproblem, but decomposing the subproblem in this way does not have " no aftereffects " Suppose F(n) = x, but there may be Multiple sequences satisfy F(n) = x. The last element of some sequences is smaller than an+1, then adding an+1 can form a longer ascending subsequence; in some sequences, the last element is not smaller than an+1... The future depends on how to reach the state n impact, does not meet the "no aftereffects".

Correct way to find:
"Find the length of the longest ascending subsequence with ak (k=1, 2, 3...N) as the end point" The rightmost number in an ascending subsequence is called the "end point" of the subsequence . Although this sub-problem is not exactly the same as the original problem, as long as the N sub-problems are solved, the largest solution of the N sub-problems is the solution of the whole problem.

2. Determine the status

The subproblems are related to only one variable - the position of the numbers. Therefore, the position k of the number in the sequence is the "state", and the "value" corresponding to the state k is the length of the longest ascending subsequence with ak as the "end point". There are a total of N states.

3. Find out the state transition equation

maxLen (k) represents the length of the longest ascending subsequence with ak as the "end point"
then:
maxLen (1) = 1
maxLen (k) = max { maxLen (i): 1<=i < k and ai < ak and k≠1 } + 1 If no such i can be found, then maxLen(k) = 1
The value of maxLen(k) is the ascending subsequence with the largest length and the “end point” on the left side of ak. add 1 to the length. Because any subsequence whose "end point" to the left of ak is less than ak, adding ak can form a longer ascending subsequence.

"Everyone is for me" recursive dynamic return program

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 using namespace std;
 5 int main()
 6 {
 7     int a[1010],n;
 8     int maxlen[1010];
 9     cin>>n;
10     for(int i=1;i<=n;i++)
11     {
12         cin>>a[i];
13         maxlen[i]=1;
14     }
15     for(int i=2;i<=n;i++)
16     {
17         for(int j=1;j<i;j++)
18         {
19             if(a[i]>a[j])
20             {
21                 maxlen[i]=max(maxlen[i],maxlen[j]+1);
22 
23             }
24         }
25     }
26     cout<<* max_element(maxlen+1,maxlen+1+n)<<endl;
27     return 0;
28 }

 

 

2. The longest ascending subsequence (LIS algorithm (nlong(n)))

Let A[t] denote the t-th number in the sequence, and F[t] denote the length of the longest ascending subsequence ending in t in the segment from 1 to t. Initially, let F[t] = 0(t = 1, 2, …, len(A)). Then there is the dynamic programming equation: F[t] = max{1, F[j] + 1} (j = 1, 2, …, t - 1, and A[j] < A[t]).

Now, we carefully consider the situation when calculating F[t]. Suppose there are two elements A[x] and A[y] such that
(1)x < y < t
(2)A[x] < A[y] < A[t]
(3)F[x] = F [y]

At this time, the same F[t] value can be obtained by selecting F[x] and selecting F[y], then, in this position of the longest ascending subsequence, should choose A[x] or should choose A[y] ]Woolen cloth?

Obviously, choosing A[x] is better than choosing A[y]. Because due to condition (2), in the segment A[x+1] ... A[t-1], if there is A[z], A[x] < A[z] < a[y], then the same as choosing A[y] will result in a longer ascending subsequence.
According to condition (3), we will get a revelation: classify according to the value of F[]. For each value k of F[], we only need to keep the minimum value among all A[t] that satisfy F[t] = k. Let D[k] record this value, that is, D[k] = min{A[t]} (F[t] = k).

Note two characteristics of D[]:
(1) The value of D[k] is monotonically non-decreasing in the whole calculation process.
(2) The values ​​of D[] are ordered, that is, 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,则有A [t] <= D[k],将A[t]接在D[j]后将得到一个更长的上升子序列,更新D[k] = A[t]。最后,len即为所要求的最长上 升子序列的长度。

在 上述算法中,若使用朴素的顺序查找在D[1]..D[len]查找,由于共有O(n)个元素需要计算,每次计算时的复杂度是O(n),则整个算法的 时间复杂度为O(n^2),与原来的算法相比没有任何进步。但是由于D[]的特点(2),我们在D[]中查找时,可以使用二分查找高效地完成,则整个算法 的时间复杂度下降为O(nlogn),有了非常显著的提高。需要注意的是,D[]在算法结束后记录的并不是一个符合题意的最长上升子序列!

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<iostream>
 5 using namespace std;
 6 
 7 const int mx=100005;
 8 int d[mx];
 9 int main()
10 {
11     int n, T;
12     while (scanf("%d",&n)!=EOF)
13     {
14         memset(d, 0, sizeof(d));
15         int len = 0, x;
16         for (int i=0;i<n;i++)
17         {
18             scanf("%d", &x);
19             if(i == 0)d[++len] = x;
20             else
21             {
22                 if(x > d[len])d[++len] = x;///如果是不下降,这里改为>=
23                 else{
24                     int pos = lower_bound(d + 1, d + len, x) - d;///如果是不下降,这里改为upper_bound(d + 1, d + len, x) - d;
25                     d[pos] = x;
26                 }
27             }
28         }
29         printf("%d\n",len);
30     }
31 }

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325690207&siteId=291194637