This is a classic DP question, also known as lis (longest increasing subsequence. There are two basic algorithms for complexity: O (N * logn) and O (N ^ 2 ).
A.
O (N ^ 2) algorithm analysis is as follows:
(A [1]... A [n] stores all input values)
1. For a [n], because it is the last number, when searching from a [n], there is only a child sequence with a length of 1 and no descent;
2. If you start searching from a [n-1], there are two possibilities:
(1) If a [n-1] <A [n], there is a child sequence a [n-1], a [n] with a length of 2.
(2) If a [n-1]> A [n], there is a child sequence a [n-1] or a [n] with a length of 1.
3. Generally, if the subsequence starts from a [T], the maximum length of the subsequence is not decreased at this time, which should be obtained using the following method:
In a [t + 1], a [T + 2],... in a [n], find a child sequence that is larger than a [T] and is the longest and does not drop as its successor.
4. Define an array for algorithm needs:
Int d [N] [3];
D [T] [0] indicates a [T];
D [T] [1] indicates the maximum length of the subsequence that reaches N from position I;
D [T] [2] indicates the next position of the subsequence starting from position I.
The implementation code is as follows:
# Include <iostream>
Using namespace STD;
Int main ()
{
Int I, j, n, a [100], B [100], Max;
While (CIN> N)
{
For (I = 0; I <n; I ++)
Cin> A [I];
B [0] = 1; // initialization. the maximum length of the Child sequence ending with a [0] is 1.
For (I = 1; I <n; I ++)
{
B [I] = 1; // the minimum value of B [I] is 1.
For (j = 0; j <I; j ++)
If (A [I]> A [J] & B [J] + 1> B [I])
B [I] = B [J] + 1;
}
For (max = I = 0; I <n; I ++) // obtain the length of the longest ascending subsequence of the entire sequence
If (B [I]> MAX)
Max = B [I];
Cout <max <Endl;
}
Return 0;
}
Obviously, the time complexity of this method is still O (N ^ 2 );
B.
The analysis of the O (N * logn) algorithm of the longest sub-sequence without descent is as follows:
If a [T] indicates the number of T in the sequence, F [T] indicates the length of the longest ascending subsequence ending with T from 1 to T, set f [T] = 0 (T = 1, 2 ,..., len ()). Then there is a dynamic planning 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. Assume that two elements a [X] and a [y] Meet
(1) x <Y <t
(2) A [x] <A [y] <A [T]
(3) f [x] = f [y]
Select f [X] and select f [y] to obtain the same f [T] value. Then, in the position of the longest ascending subsequence, should I select a [x] or a [y?
Obviously, selecting a [x] is better than selecting a [Y. Because of the condition (2), in a [x + 1]... in the section A [T-1], if a [Z], a [x] <A [Z] <A [Y, A longer ascending subsequence is obtained.
Then, based on the condition (3), we will get an inspiration: classification based on the value of F. For each value K of F [], we only need to retain the minimum values of all a [T] that satisfy f [T] = K. Set d [k] to record this value, that is, d [k] = min {A [T]} (F [T] = K ).
Note the two features of d:
(1) The value of d [k] does not increase monotonically throughout the calculation process.
(2) The values of d [] are ordered, that is, d [1] <D [2] <D [3] <... <D [N].
Using d [], we can obtain another method to calculate the maximum length of the ascending subsequence. Set the maximum length of the obtained ascending subsequence to Len. First, judge a [T] And d [Len]. If a [T]> d [Len], a [T] is connected to d [Len] and a longer ascending subsequence is obtained. Len = Len + 1, d [Len] = A [T]; otherwise, in D [1] .. in D [Len], find the largest J, satisfying d [J] <A [T]. For k = J + 1, a [T] <= d [k] is generated. After a [T] is connected to d [J], a longer ascending subsequence is obtained, update d [k] = A [T]. Finally, Len is the maximum length of the ascending sub-sequence.
In the above algorithm, if you use the simple sequence to search for the data in the d [1] .. d [Len] search, because a total of O (n) elements need to be calculated, the complexity of each calculation is O (n ), the time complexity of the entire algorithm is O (n ^ 2), which is not improved compared with the original algorithm. However, due to the features of d [] (2), we can use binary lookup to efficiently complete the search in D, the time complexity of the entire algorithm is reduced to O (nlogn), which is greatly improved. Note that after the algorithm is completed, d [] does not record the longest ascending subsequence that matches the question!
# Include <iostream>
Using namespace STD;
Int find (int * a, int Len, int N) // if the return value is X, a [x]> = n> A [x-1]
{
Int left = 0, Right = Len, mid = (left + right)/2;
While (left <= right)
{
If (n> A [Mid]) Left = Mid + 1;
Else if (n <A [Mid]) Right = mid-1;
Else return mid;
Mid = (left + right)/2;
}
Return left;
}
Void fill (int * a, int N)
{
For (INT I = 0; I <= N; I ++)
A [I] = 1000;
}
Int main ()
{
Int Max, I, j, n, a [100], B [100], C [100];
While (CIN> N)
{
Fill (C, N + 1 );
For (I = 0; I <n; I ++)
Cin> A [I];
C [0] =-1 ;//................................................ 1
C [1] = A [0]; //…… 2
B [0] = 1 ;//................................................ 3
For (I = 1; I <n; I ++ )//.................................... 4
{
J = find (C, N + 1, a [I]); // ...... 5
C [J] = A [I]; //…… 6
B [I] = J ;//.......................................... 7
}
For (max = I = 0; I <n; I ++ )//.................................... 8
If (B [I]> MAX)
Max = B [I];
Cout <max <Endl;
}
Return 0;
}
For this program, we can use loop Invariants in the introduction to algorithms to help us understand it.
Loop Invariant: 1. c increases monotonically after each loop. (This property determines that binary search can be used)
2. After each loop, C [I] always saves the last element of the ascending sub-sequence with the length of I. If the length is the ascending sub-sequence of I
There are multiple columns and the smallest element at the end of the column is saved. (This property determines the premise that 3rd attributes are valid)
3. After each loop, B [I] always saves the longest ascending subsequence ending with a [I.
Initialization: 1. before entering the loop, C [0] =-1, C [1] = A [0], C's other elements are 1000, and C is monotonic increasing;
2. Before entering the loop, C [1] = A [0] saves the last element of the incremental sequence with a length of 1, and the length is 1
And C [1] is the smallest;
3. before entering the loop, B [0] = 1. At this time, the length of the longest incrementing subsequence ending with a [0] is 1.
Maintenance: 1. If C increases monotonically before the nth cycle, the value of C changes only in row 6th.
C is monotonically incrementing before entering the loop and the nature of the find function can be seen (see the comment of find ),
C [J + 1]> C [J]> = A [I]> C [J-1], so after the value of C [J] is updated to a [I, c [J + 1]> C [J]> C [J-1] is still
Stand, that is, C is still monotonically increasing;
2. in a loop, the value of C changes only in row 6th. After C [J]> = A [I] is updated to a [I, the value of C [J] only changes
Small values do not increase, because C [J] is the smallest value before entering the loop, C [J] is updated to a smaller a [I] in the loop. When
At this time, the value of C [J] is still the smallest;
3. In a loop, the value of B [I] Changes in row 7th, because of the nature of Loop Invariant 2, the return value of the find Function
For J has: C [J-1] <A [I] <= C [J], which indicates that C [J-1] is less than a [I, the ascending subsequence ending with C [J-1] has the largest
The length, that is, the J-1. After a [I] is connected to C [J-1], the longest incrementing subsequence ending with a [I] is obtained, and the length is (J-1) + 1 = J;
Termination: After the loop is complete, I = n-1, B [0], B [1],..., the values of B [n-1] have been obtained, that is, a [0], a [1],..., the longest delivery at the end of a [n-1]
The length of the incrementing sub-sequence has been obtained, and then the longest incrementing sub-sequence of the entire array is obtained through the cycle of 8th rows.
By carefully analyzing the code above, we can find that after each loop ends, assuming that C [1], C [2], C [3],..., c [Len] value, then the length of the longest incrementing sub-sequence is Len. Therefore, the code above can be simplified, that is, array B is not required for auxiliary storage, the cycle of the 8th rows can also be omitted.
# Include <iostream>
Using namespace STD;
Int find (int * a, int Len, int N) // binary search after modification. If the return value is X, a [x]> = N
{
Int left = 0, Right = Len, mid = (left + right)/2;
While (left <= right)
{
If (n> A [Mid]) Left = Mid + 1;
Else if (n <A [Mid]) Right = mid-1;
Else return mid;
Mid = (left + right)/2;
}
Return left;
}
Int main ()
{
Int n, a [100], C [100], I, j, Len; // a new variable Len, stores the maximum subscript of the element whose values have been obtained in C after each loop ends.
While (CIN> N)
{
For (I = 0; I <n; I ++)
Cin> A [I];
B [0] = 1;
C [0] =-1;
C [1] = A [0];
Len = 1; // at this time, only C [1] is obtained. The maximum length of the ascending subsequence is 1.
For (I = 1; I <n; I ++)
{
J = find (C, Len, a [I]);
C [J] = A [I];
If (j> Len) // update Len. Additionally, we can add that J is only 1 larger than Len in binary search.
Len = J; // update Len
}
Cout <Len <Endl;
}
Return 0;
}