Tôi có một bộ số nguyên. Tôi muốn tìm phần tiếp theo tăng dài nhất của tập hợp đó bằng lập trình động.
Tôi có một bộ số nguyên. Tôi muốn tìm phần tiếp theo tăng dài nhất của tập hợp đó bằng lập trình động.
Câu trả lời:
OK, tôi sẽ mô tả đầu tiên giải pháp đơn giản nhất là O (N ^ 2), trong đó N là kích thước của bộ sưu tập. Cũng tồn tại một giải pháp O (N log N), mà tôi cũng sẽ mô tả. Nhìn vào đây cho nó ở phần Thuật toán hiệu quả.
Tôi sẽ giả sử các chỉ số của mảng là từ 0 đến N - 1. Vì vậy, hãy xác định DP[i]
là độ dài của LIS (chuỗi tăng dài nhất) kết thúc tại phần tử có chỉ mục i
. Để tính toán, DP[i]
chúng tôi xem xét tất cả các chỉ số j < i
và kiểm tra cả nếu DP[j] + 1 > DP[i]
và array[j] < array[i]
(chúng tôi muốn nó tăng lên). Nếu điều này là đúng, chúng ta có thể cập nhật tối ưu hiện tại cho DP[i]
. Để tìm tối ưu toàn cầu cho mảng, bạn có thể lấy giá trị tối đa từ đó DP[0...N - 1]
.
int maxLength = 1, bestEnd = 0;
DP[0] = 1;
prev[0] = -1;
for (int i = 1; i < N; i++)
{
DP[i] = 1;
prev[i] = -1;
for (int j = i - 1; j >= 0; j--)
if (DP[j] + 1 > DP[i] && array[j] < array[i])
{
DP[i] = DP[j] + 1;
prev[i] = j;
}
if (DP[i] > maxLength)
{
bestEnd = i;
maxLength = DP[i];
}
}
Tôi sử dụng mảng prev
để có thể tìm thấy chuỗi thực tế không chỉ chiều dài của nó. Chỉ cần quay lại đệ quy từ bestEnd
trong một vòng lặp bằng cách sử dụng prev[bestEnd]
. Các -1
giá trị là một dấu hiệu dừng lại.
O(N log N)
giải pháp hiệu quả hơn :Hãy S[pos]
được định nghĩa là số nguyên nhỏ nhất kết thúc chuỗi độ dài tăng dần pos
. Bây giờ lặp lại qua mọi số nguyên X
của tập hợp đầu vào và làm như sau:
Nếu X
> phần tử cuối cùng trong S
, sau đó nối X
vào cuối S
. Điều cần thiết này có nghĩa là chúng tôi đã tìm thấy một lớn nhất mới LIS
.
Mặt khác, tìm phần tử nhỏ nhất S
, >=
hơn X
và thay đổi nó thành X
. Bởi vì S
được sắp xếp bất cứ lúc nào, phần tử có thể được tìm thấy bằng cách sử dụng tìm kiếm nhị phân trong log(N)
.
Tổng thời gian chạy - N
số nguyên và tìm kiếm nhị phân cho mỗi trong số chúng - N * log (N) = O (N log N)
Bây giờ hãy làm một ví dụ thực tế:
Bộ sưu tập các số nguyên:
2 6 3 4 1 2 9 5 8
Các bước:
0. S = {} - Initialize S to the empty set
1. S = {2} - New largest LIS
2. S = {2, 6} - New largest LIS
3. S = {2, 3} - Changed 6 to 3
4. S = {2, 3, 4} - New largest LIS
5. S = {1, 3, 4} - Changed 2 to 1
6. S = {1, 2, 4} - Changed 3 to 2
7. S = {1, 2, 4, 9} - New largest LIS
8. S = {1, 2, 4, 5} - Changed 9 to 5
9. S = {1, 2, 4, 5, 8} - New largest LIS
Vậy độ dài của LIS là 5
(kích thước của S).
Để xây dựng lại thực tế, LIS
chúng ta sẽ lại sử dụng một mảng cha. Hãy parent[i]
là tiền thân của phần tử có chỉ mục i
ở phần LIS
cuối tại phần tử có chỉ mục i
.
Để làm cho mọi thứ đơn giản hơn, chúng ta có thể giữ trong mảng S
, không phải là số nguyên thực tế, mà là các chỉ số (vị trí) của chúng trong tập hợp. Chúng tôi không giữ {1, 2, 4, 5, 8}
, nhưng giữ {4, 5, 3, 7, 8}
.
Đó là đầu vào [4] = 1 , đầu vào [5] = 2 , đầu vào [3] = 4 , đầu vào [7] = 5 , đầu vào [8] = 8 .
Nếu chúng tôi cập nhật đúng mảng cha, LIS thực tế là:
input[S[lastElementOfS]],
input[parent[S[lastElementOfS]]],
input[parent[parent[S[lastElementOfS]]]],
........................................
Bây giờ đến điều quan trọng - làm thế nào để chúng tôi cập nhật mảng cha? Có hai lựa chọn:
Nếu X
> phần tử cuối cùng trong S
, sau đó parent[indexX] = indexLastElement
. Điều này có nghĩa là cha mẹ của phần tử mới nhất là phần tử cuối cùng. Chúng tôi chỉ chuẩn bị X
cho đến cuối S
.
Mặt khác, tìm chỉ mục của phần tử nhỏ nhất S
, >=
hơn X
và thay đổi nó thành X
. Đây parent[indexX] = S[index - 1]
.
DP[j] + 1 == DP[i]
sau đó DP[i]
sẽ không trở nên tốt hơn với DP[i] = DP[j] + 1
. Chúng tôi đang cố gắng tối ưu hóa DP[i]
.
[1,2,5,8]
, 4 đến trước 1 trong mảng, LIS có thể [1,2,4,5,8]
như thế nào?
[2,3,4,5,8]
. Đọc kỹ - S
mảng DOES NOT
đại diện cho một chuỗi thực tế. Let S[pos] be defined as the smallest integer that ends an increasing sequence of length pos.
Lời giải thích của Petar Minchev đã giúp tôi làm rõ mọi thứ, nhưng thật khó để tôi phân tích mọi thứ là gì, vì vậy tôi đã thực hiện Python với các tên biến mô tả quá nhiều và rất nhiều bình luận. Tôi đã làm một giải pháp đệ quy ngây thơ, giải pháp O (n ^ 2) và giải pháp O (n log n).
Tôi hy vọng nó sẽ giúp làm sáng tỏ các thuật toán!
def recursive_solution(remaining_sequence, bigger_than=None):
"""Finds the longest increasing subsequence of remaining_sequence that is
bigger than bigger_than and returns it. This solution is O(2^n)."""
# Base case: nothing is remaining.
if len(remaining_sequence) == 0:
return remaining_sequence
# Recursive case 1: exclude the current element and process the remaining.
best_sequence = recursive_solution(remaining_sequence[1:], bigger_than)
# Recursive case 2: include the current element if it's big enough.
first = remaining_sequence[0]
if (first > bigger_than) or (bigger_than is None):
sequence_with = [first] + recursive_solution(remaining_sequence[1:], first)
# Choose whichever of case 1 and case 2 were longer.
if len(sequence_with) >= len(best_sequence):
best_sequence = sequence_with
return best_sequence
def dynamic_programming_solution(sequence):
"""Finds the longest increasing subsequence in sequence using dynamic
programming. This solution is O(n^2)."""
longest_subsequence_ending_with = []
backreference_for_subsequence_ending_with = []
current_best_end = 0
for curr_elem in range(len(sequence)):
# It's always possible to have a subsequence of length 1.
longest_subsequence_ending_with.append(1)
# If a subsequence is length 1, it doesn't have a backreference.
backreference_for_subsequence_ending_with.append(None)
for prev_elem in range(curr_elem):
subsequence_length_through_prev = (longest_subsequence_ending_with[prev_elem] + 1)
# If the prev_elem is smaller than the current elem (so it's increasing)
# And if the longest subsequence from prev_elem would yield a better
# subsequence for curr_elem.
if ((sequence[prev_elem] < sequence[curr_elem]) and
(subsequence_length_through_prev >
longest_subsequence_ending_with[curr_elem])):
# Set the candidate best subsequence at curr_elem to go through prev.
longest_subsequence_ending_with[curr_elem] = (subsequence_length_through_prev)
backreference_for_subsequence_ending_with[curr_elem] = prev_elem
# If the new end is the best, update the best.
if (longest_subsequence_ending_with[curr_elem] >
longest_subsequence_ending_with[current_best_end]):
current_best_end = curr_elem
# Output the overall best by following the backreferences.
best_subsequence = []
current_backreference = current_best_end
while current_backreference is not None:
best_subsequence.append(sequence[current_backreference])
current_backreference = (backreference_for_subsequence_ending_with[current_backreference])
best_subsequence.reverse()
return best_subsequence
def find_smallest_elem_as_big_as(sequence, subsequence, elem):
"""Returns the index of the smallest element in subsequence as big as
sequence[elem]. sequence[elem] must not be larger than every element in
subsequence. The elements in subsequence are indices in sequence. Uses
binary search."""
low = 0
high = len(subsequence) - 1
while high > low:
mid = (high + low) / 2
# If the current element is not as big as elem, throw out the low half of
# sequence.
if sequence[subsequence[mid]] < sequence[elem]:
low = mid + 1
# If the current element is as big as elem, throw out everything bigger, but
# keep the current element.
else:
high = mid
return high
def optimized_dynamic_programming_solution(sequence):
"""Finds the longest increasing subsequence in sequence using dynamic
programming and binary search (per
http://en.wikipedia.org/wiki/Longest_increasing_subsequence). This solution
is O(n log n)."""
# Both of these lists hold the indices of elements in sequence and not the
# elements themselves.
# This list will always be sorted.
smallest_end_to_subsequence_of_length = []
# This array goes along with sequence (not
# smallest_end_to_subsequence_of_length). Following the corresponding element
# in this array repeatedly will generate the desired subsequence.
parent = [None for _ in sequence]
for elem in range(len(sequence)):
# We're iterating through sequence in order, so if elem is bigger than the
# end of longest current subsequence, we have a new longest increasing
# subsequence.
if (len(smallest_end_to_subsequence_of_length) == 0 or
sequence[elem] > sequence[smallest_end_to_subsequence_of_length[-1]]):
# If we are adding the first element, it has no parent. Otherwise, we
# need to update the parent to be the previous biggest element.
if len(smallest_end_to_subsequence_of_length) > 0:
parent[elem] = smallest_end_to_subsequence_of_length[-1]
smallest_end_to_subsequence_of_length.append(elem)
else:
# If we can't make a longer subsequence, we might be able to make a
# subsequence of equal size to one of our earlier subsequences with a
# smaller ending number (which makes it easier to find a later number that
# is increasing).
# Thus, we look for the smallest element in
# smallest_end_to_subsequence_of_length that is at least as big as elem
# and replace it with elem.
# This preserves correctness because if there is a subsequence of length n
# that ends with a number smaller than elem, we could add elem on to the
# end of that subsequence to get a subsequence of length n+1.
location_to_replace = find_smallest_elem_as_big_as(sequence, smallest_end_to_subsequence_of_length, elem)
smallest_end_to_subsequence_of_length[location_to_replace] = elem
# If we're replacing the first element, we don't need to update its parent
# because a subsequence of length 1 has no parent. Otherwise, its parent
# is the subsequence one shorter, which we just added onto.
if location_to_replace != 0:
parent[elem] = (smallest_end_to_subsequence_of_length[location_to_replace - 1])
# Generate the longest increasing subsequence by backtracking through parent.
curr_parent = smallest_end_to_subsequence_of_length[-1]
longest_increasing_subsequence = []
while curr_parent is not None:
longest_increasing_subsequence.append(sequence[curr_parent])
curr_parent = parent[curr_parent]
longest_increasing_subsequence.reverse()
return longest_increasing_subsequence
bisect
. Để chứng minh làm thế nào một thuật toán hoạt động và đặc điểm hiệu suất của nó, tôi đã cố gắng giữ mọi thứ nguyên thủy nhất có thể.
Nói về giải pháp DP, tôi thấy ngạc nhiên khi không ai đề cập đến thực tế rằng LIS có thể được giảm xuống LCS . Tất cả những gì bạn cần làm là sắp xếp bản sao của chuỗi gốc, loại bỏ tất cả các bản sao và thực hiện LCS của chúng. Trong mã giả đó là:
def LIS(S):
T = sort(S)
T = removeDuplicates(T)
return LCS(S, T)
Và thực hiện đầy đủ bằng văn bản trong Go. Bạn không cần duy trì toàn bộ ma trận n ^ 2 DP nếu bạn không cần xây dựng lại giải pháp.
func lcs(arr1 []int) int {
arr2 := make([]int, len(arr1))
for i, v := range arr1 {
arr2[i] = v
}
sort.Ints(arr1)
arr3 := []int{}
prev := arr1[0] - 1
for _, v := range arr1 {
if v != prev {
prev = v
arr3 = append(arr3, v)
}
}
n1, n2 := len(arr1), len(arr3)
M := make([][]int, n2 + 1)
e := make([]int, (n1 + 1) * (n2 + 1))
for i := range M {
M[i] = e[i * (n1 + 1):(i + 1) * (n1 + 1)]
}
for i := 1; i <= n2; i++ {
for j := 1; j <= n1; j++ {
if arr2[j - 1] == arr3[i - 1] {
M[i][j] = M[i - 1][j - 1] + 1
} else if M[i - 1][j] > M[i][j - 1] {
M[i][j] = M[i - 1][j]
} else {
M[i][j] = M[i][j - 1]
}
}
}
return M[n2][n1]
}
Việc triển khai C ++ sau đây cũng bao gồm một số mã xây dựng chuỗi tăng dần dài nhất thực tế bằng cách sử dụng một mảng được gọi prev
.
std::vector<int> longest_increasing_subsequence (const std::vector<int>& s)
{
int best_end = 0;
int sz = s.size();
if (!sz)
return std::vector<int>();
std::vector<int> prev(sz,-1);
std::vector<int> memo(sz, 0);
int max_length = std::numeric_limits<int>::min();
memo[0] = 1;
for ( auto i = 1; i < sz; ++i)
{
for ( auto j = 0; j < i; ++j)
{
if ( s[j] < s[i] && memo[i] < memo[j] + 1 )
{
memo[i] = memo[j] + 1;
prev[i] = j;
}
}
if ( memo[i] > max_length )
{
best_end = i;
max_length = memo[i];
}
}
// Code that builds the longest increasing subsequence using "prev"
std::vector<int> results;
results.reserve(sz);
std::stack<int> stk;
int current = best_end;
while (current != -1)
{
stk.push(s[current]);
current = prev[current];
}
while (!stk.empty())
{
results.push_back(stk.top());
stk.pop();
}
return results;
}
Thực hiện không có ngăn xếp chỉ đảo ngược vector
#include <iostream>
#include <vector>
#include <limits>
std::vector<int> LIS( const std::vector<int> &v ) {
auto sz = v.size();
if(!sz)
return v;
std::vector<int> memo(sz, 0);
std::vector<int> prev(sz, -1);
memo[0] = 1;
int best_end = 0;
int max_length = std::numeric_limits<int>::min();
for (auto i = 1; i < sz; ++i) {
for ( auto j = 0; j < i ; ++j) {
if (s[j] < s[i] && memo[i] < memo[j] + 1) {
memo[i] = memo[j] + 1;
prev[i] = j;
}
}
if(memo[i] > max_length) {
best_end = i;
max_length = memo[i];
}
}
// create results
std::vector<int> results;
results.reserve(v.size());
auto current = best_end;
while (current != -1) {
results.push_back(s[current]);
current = prev[current];
}
std::reverse(results.begin(), results.end());
return results;
}
Dưới đây là ba bước đánh giá vấn đề theo quan điểm lập trình động:
Nếu chúng ta lấy một chuỗi ví dụ {0, 8, 2, 3, 7, 9}, tại chỉ mục:
Đây là mã C ++ 11 đang hoạt động:
#include <iostream>
#include <vector>
int getLongestIncSub(const std::vector<int> &sequence, size_t index, std::vector<std::vector<int>> &sub) {
if(index == 0) {
sub.push_back(std::vector<int>{sequence[0]});
return 1;
}
size_t longestSubSeq = getLongestIncSub(sequence, index - 1, sub);
std::vector<std::vector<int>> tmpSubSeq;
for(std::vector<int> &subSeq : sub) {
if(subSeq[subSeq.size() - 1] < sequence[index]) {
std::vector<int> newSeq(subSeq);
newSeq.push_back(sequence[index]);
longestSubSeq = std::max(longestSubSeq, newSeq.size());
tmpSubSeq.push_back(newSeq);
}
}
std::copy(tmpSubSeq.begin(), tmpSubSeq.end(),
std::back_insert_iterator<std::vector<std::vector<int>>>(sub));
return longestSubSeq;
}
int getLongestIncSub(const std::vector<int> &sequence) {
std::vector<std::vector<int>> sub;
return getLongestIncSub(sequence, sequence.size() - 1, sub);
}
int main()
{
std::vector<int> seq{0, 8, 2, 3, 7, 9};
std::cout << getLongestIncSub(seq);
return 0;
}
Đây là một triển khai Scala của thuật toán O (n ^ 2):
object Solve {
def longestIncrSubseq[T](xs: List[T])(implicit ord: Ordering[T]) = {
xs.foldLeft(List[(Int, List[T])]()) {
(sofar, x) =>
if (sofar.isEmpty) List((1, List(x)))
else {
val resIfEndsAtCurr = (sofar, xs).zipped map {
(tp, y) =>
val len = tp._1
val seq = tp._2
if (ord.lteq(y, x)) {
(len + 1, x :: seq) // reversely recorded to avoid O(n)
} else {
(1, List(x))
}
}
sofar :+ resIfEndsAtCurr.maxBy(_._1)
}
}.maxBy(_._1)._2.reverse
}
def main(args: Array[String]) = {
println(longestIncrSubseq(List(
0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15)))
}
}
Đây là một triển khai JAVA O (n ^ 2) khác. Không có đệ quy / ghi nhớ để tạo ra chuỗi con thực tế. Chỉ là một mảng chuỗi lưu trữ LIS thực tế ở mọi giai đoạn và một mảng để lưu trữ độ dài của LIS cho mỗi phần tử. Khá dễ dàng. Có một cái nhìn:
import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
* Created by Shreyans on 4/16/2015
*/
class LNG_INC_SUB//Longest Increasing Subsequence
{
public static void main(String[] args) throws Exception
{
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
System.out.println("Enter Numbers Separated by Spaces to find their LIS\n");
String[] s1=br.readLine().split(" ");
int n=s1.length;
int[] a=new int[n];//Array actual of Numbers
String []ls=new String[n];// Array of Strings to maintain LIS for every element
for(int i=0;i<n;i++)
{
a[i]=Integer.parseInt(s1[i]);
}
int[]dp=new int[n];//Storing length of max subseq.
int max=dp[0]=1;//Defaults
String seq=ls[0]=s1[0];//Defaults
for(int i=1;i<n;i++)
{
dp[i]=1;
String x="";
for(int j=i-1;j>=0;j--)
{
//First check if number at index j is less than num at i.
// Second the length of that DP should be greater than dp[i]
// -1 since dp of previous could also be one. So we compare the dp[i] as empty initially
if(a[j]<a[i]&&dp[j]>dp[i]-1)
{
dp[i]=dp[j]+1;//Assigning temp length of LIS. There may come along a bigger LIS of a future a[j]
x=ls[j];//Assigning temp LIS of a[j]. Will append a[i] later on
}
}
x+=(" "+a[i]);
ls[i]=x;
if(dp[i]>max)
{
max=dp[i];
seq=ls[i];
}
}
System.out.println("Length of LIS is: " + max + "\nThe Sequence is: " + seq);
}
}
Mã đang hoạt động: http://ideone.com/sBiOQx
Điều này có thể được giải quyết trong O (n ^ 2) bằng Lập trình động. Mã Python cho cùng sẽ giống như: -
def LIS(numlist):
LS = [1]
for i in range(1, len(numlist)):
LS.append(1)
for j in range(0, i):
if numlist[i] > numlist[j] and LS[i]<=LS[j]:
LS[i] = 1 + LS[j]
print LS
return max(LS)
numlist = map(int, raw_input().split(' '))
print LIS(numlist)
Đối với đầu vào:5 19 5 81 50 28 29 1 83 23
đầu ra sẽ là:[1, 2, 1, 3, 3, 3, 4, 1, 5, 3]
5
List_index của danh sách đầu ra là list_index của danh sách đầu vào. Giá trị tại một list_index đã cho trong danh sách đầu ra biểu thị độ dài tiếp theo tăng dài nhất cho list_index đó.
đây là java O (nlogn) thực hiện
import java.util.Scanner;
public class LongestIncreasingSeq {
private static int binarySearch(int table[],int a,int len){
int end = len-1;
int beg = 0;
int mid = 0;
int result = -1;
while(beg <= end){
mid = (end + beg) / 2;
if(table[mid] < a){
beg=mid+1;
result = mid;
}else if(table[mid] == a){
return len-1;
}else{
end = mid-1;
}
}
return result;
}
public static void main(String[] args) {
// int[] t = {1, 2, 5,9,16};
// System.out.println(binarySearch(t , 9, 5));
Scanner in = new Scanner(System.in);
int size = in.nextInt();//4;
int A[] = new int[size];
int table[] = new int[A.length];
int k = 0;
while(k<size){
A[k++] = in.nextInt();
if(k<size-1)
in.nextLine();
}
table[0] = A[0];
int len = 1;
for (int i = 1; i < A.length; i++) {
if(table[0] > A[i]){
table[0] = A[i];
}else if(table[len-1]<A[i]){
table[len++]=A[i];
}else{
table[binarySearch(table, A[i],len)+1] = A[i];
}
}
System.out.println(len);
}
}
Đây là một triển khai Java trong O (n ^ 2). Tôi chỉ không sử dụng Tìm kiếm nhị phân để tìm phần tử nhỏ nhất trong S, đó là> = hơn X. Tôi chỉ sử dụng một vòng lặp for. Sử dụng Tìm kiếm nhị phân sẽ làm cho sự phức tạp tại O (n logn)
public static void olis(int[] seq){
int[] memo = new int[seq.length];
memo[0] = seq[0];
int pos = 0;
for (int i=1; i<seq.length; i++){
int x = seq[i];
if (memo[pos] < x){
pos++;
memo[pos] = x;
} else {
for(int j=0; j<=pos; j++){
if (memo[j] >= x){
memo[j] = x;
break;
}
}
}
//just to print every step
System.out.println(Arrays.toString(memo));
}
//the final array with the LIS
System.out.println(Arrays.toString(memo));
System.out.println("The length of lis is " + (pos + 1));
}
kiểm tra mã trong java để tăng thứ tự dài nhất với các phần tử mảng
/**
** Java Program to implement Longest Increasing Subsequence Algorithm
**/
import java.util.Scanner;
/** Class LongestIncreasingSubsequence **/
class LongestIncreasingSubsequence
{
/** function lis **/
public int[] lis(int[] X)
{
int n = X.length - 1;
int[] M = new int[n + 1];
int[] P = new int[n + 1];
int L = 0;
for (int i = 1; i < n + 1; i++)
{
int j = 0;
/** Linear search applied here. Binary Search can be applied too.
binary search for the largest positive j <= L such that
X[M[j]] < X[i] (or set j = 0 if no such value exists) **/
for (int pos = L ; pos >= 1; pos--)
{
if (X[M[pos]] < X[i])
{
j = pos;
break;
}
}
P[i] = M[j];
if (j == L || X[i] < X[M[j + 1]])
{
M[j + 1] = i;
L = Math.max(L,j + 1);
}
}
/** backtrack **/
int[] result = new int[L];
int pos = M[L];
for (int i = L - 1; i >= 0; i--)
{
result[i] = X[pos];
pos = P[pos];
}
return result;
}
/** Main Function **/
public static void main(String[] args)
{
Scanner scan = new Scanner(System.in);
System.out.println("Longest Increasing Subsequence Algorithm Test\n");
System.out.println("Enter number of elements");
int n = scan.nextInt();
int[] arr = new int[n + 1];
System.out.println("\nEnter "+ n +" elements");
for (int i = 1; i <= n; i++)
arr[i] = scan.nextInt();
LongestIncreasingSubsequence obj = new LongestIncreasingSubsequence();
int[] result = obj.lis(arr);
/** print result **/
System.out.print("\nLongest Increasing Subsequence : ");
for (int i = 0; i < result.length; i++)
System.out.print(result[i] +" ");
System.out.println();
}
}
Điều này có thể được giải quyết trong O (n ^ 2) bằng lập trình động.
Xử lý các phần tử đầu vào theo thứ tự và duy trì một danh sách các bộ dữ liệu cho từng phần tử. Mỗi bộ (A, B), đối với phần tử i sẽ biểu thị, A = độ dài của chuỗi con tăng dài nhất kết thúc tại i và B = chỉ số của tiền thân của danh sách [i] trong chuỗi con tăng dài nhất kết thúc tại danh sách [i ].
Bắt đầu từ phần tử 1, danh sách tuple cho phần tử 1 sẽ là [(1,0)] cho phần tử i, quét danh sách 0..i và tìm danh sách phần tử [k] sao cho danh sách đó [k] <list [i] , giá trị của A cho phần tử i, Ai sẽ là Ak + 1 và Bi sẽ là k. Nếu có nhiều phần tử như vậy, hãy thêm chúng vào danh sách các bộ dữ liệu cho phần tử i.
Cuối cùng, tìm tất cả các phần tử có giá trị tối đa là A (độ dài của LIS kết thúc tại phần tử) và quay lại bằng cách sử dụng các bộ dữ liệu để lấy danh sách.
Tôi đã chia sẻ mã cho cùng tại http://www.edufyme.com/code/?id=66f041e16a60928b05a7e228a89c3799
O (n ^ 2) java thực hiện:
void LIS(int arr[]){
int maxCount[]=new int[arr.length];
int link[]=new int[arr.length];
int maxI=0;
link[0]=0;
maxCount[0]=0;
for (int i = 1; i < arr.length; i++) {
for (int j = 0; j < i; j++) {
if(arr[j]<arr[i] && ((maxCount[j]+1)>maxCount[i])){
maxCount[i]=maxCount[j]+1;
link[i]=j;
if(maxCount[i]>maxCount[maxI]){
maxI=i;
}
}
}
}
for (int i = 0; i < link.length; i++) {
System.out.println(arr[i]+" "+link[i]);
}
print(arr,maxI,link);
}
void print(int arr[],int index,int link[]){
if(link[index]==index){
System.out.println(arr[index]+" ");
return;
}else{
print(arr, link[index], link);
System.out.println(arr[index]+" ");
}
}
def longestincrsub(arr1):
n=len(arr1)
l=[1]*n
for i in range(0,n):
for j in range(0,i) :
if arr1[j]<arr1[i] and l[i]<l[j] + 1:
l[i] =l[j] + 1
l.sort()
return l[-1]
arr1=[10,22,9,33,21,50,41,60]
a=longestincrsub(arr1)
print(a)
mặc dù có một cách mà bạn có thể giải quyết điều này trong thời gian O (nlogn) (điều này giải quyết trong thời gian O (n ^ 2)) nhưng cách này mang lại cách tiếp cận lập trình động cũng tốt.
Đây là giải pháp Leetcode của tôi bằng cách sử dụng Tìm kiếm nhị phân: ->
class Solution:
def binary_search(self,s,x):
low=0
high=len(s)-1
flag=1
while low<=high:
mid=(high+low)//2
if s[mid]==x:
flag=0
break
elif s[mid]<x:
low=mid+1
else:
high=mid-1
if flag:
s[low]=x
return s
def lengthOfLIS(self, nums: List[int]) -> int:
if not nums:
return 0
s=[]
s.append(nums[0])
for i in range(1,len(nums)):
if s[-1]<nums[i]:
s.append(nums[i])
else:
s=self.binary_search(s,nums[i])
return len(s)
#include <iostream>
#include "vector"
using namespace std;
// binary search (If value not found then it will return the index where the value should be inserted)
int ceilBinarySearch(vector<int> &a,int beg,int end,int value)
{
if(beg<=end)
{
int mid = (beg+end)/2;
if(a[mid] == value)
return mid;
else if(value < a[mid])
return ceilBinarySearch(a,beg,mid-1,value);
else
return ceilBinarySearch(a,mid+1,end,value);
return 0;
}
return beg;
}
int lis(vector<int> arr)
{
vector<int> dp(arr.size(),0);
int len = 0;
for(int i = 0;i<arr.size();i++)
{
int j = ceilBinarySearch(dp,0,len-1,arr[i]);
dp[j] = arr[i];
if(j == len)
len++;
}
return len;
}
int main()
{
vector<int> arr {2, 5,-1,0,6,1,2};
cout<<lis(arr);
return 0;
}
ĐẦU RA:
4
Hậu quả gia tăng dài nhất (Java)
import java.util.*;
class ChainHighestValue implements Comparable<ChainHighestValue>{
int highestValue;
int chainLength;
ChainHighestValue(int highestValue,int chainLength) {
this.highestValue = highestValue;
this.chainLength = chainLength;
}
@Override
public int compareTo(ChainHighestValue o) {
return this.chainLength-o.chainLength;
}
}
public class LongestIncreasingSubsequenceLinkedList {
private static LinkedList<Integer> LongestSubsequent(int arr[], int size){
ArrayList<LinkedList<Integer>> seqList=new ArrayList<>();
ArrayList<ChainHighestValue> valuePairs=new ArrayList<>();
for(int i=0;i<size;i++){
int currValue=arr[i];
if(valuePairs.size()==0){
LinkedList<Integer> aList=new LinkedList<>();
aList.add(arr[i]);
seqList.add(aList);
valuePairs.add(new ChainHighestValue(arr[i],1));
}else{
try{
ChainHighestValue heighestIndex=valuePairs.stream().filter(e->e.highestValue<currValue).max(ChainHighestValue::compareTo).get();
int index=valuePairs.indexOf(heighestIndex);
seqList.get(index).add(arr[i]);
heighestIndex.highestValue=arr[i];
heighestIndex.chainLength+=1;
}catch (Exception e){
LinkedList<Integer> aList=new LinkedList<>();
aList.add(arr[i]);
seqList.add(aList);
valuePairs.add(new ChainHighestValue(arr[i],1));
}
}
}
ChainHighestValue heighestIndex=valuePairs.stream().max(ChainHighestValue::compareTo).get();
int index=valuePairs.indexOf(heighestIndex);
return seqList.get(index);
}
public static void main(String[] args){
int arry[]={5,1,3,6,11,30,32,5,3,73,79};
//int arryB[]={3,1,5,2,6,4,9};
LinkedList<Integer> LIS=LongestSubsequent(arry, arry.length);
System.out.println("Longest Incrementing Subsequence:");
for(Integer a: LIS){
System.out.print(a+" ");
}
}
}
Tôi đã triển khai LIS trong java bằng cách sử dụng Lập trình động và Ghi nhớ. Cùng với mã tôi đã thực hiện tính toán phức tạp, tức là tại sao nó là O (n Log (base2) n). Theo tôi cảm thấy giải thích lý thuyết hoặc logic là tốt nhưng trình diễn thực tế luôn tốt hơn để hiểu.
package com.company.dynamicProgramming;
import java.util.HashMap;
import java.util.Map;
public class LongestIncreasingSequence {
static int complexity = 0;
public static void main(String ...args){
int[] arr = {10, 22, 9, 33, 21, 50, 41, 60, 80};
int n = arr.length;
Map<Integer, Integer> memo = new HashMap<>();
lis(arr, n, memo);
//Display Code Begins
int x = 0;
System.out.format("Longest Increasing Sub-Sequence with size %S is -> ",memo.get(n));
for(Map.Entry e : memo.entrySet()){
if((Integer)e.getValue() > x){
System.out.print(arr[(Integer)e.getKey()-1] + " ");
x++;
}
}
System.out.format("%nAnd Time Complexity for Array size %S is just %S ", arr.length, complexity );
System.out.format( "%nWhich is equivalent to O(n Log n) i.e. %SLog(base2)%S is %S",arr.length,arr.length, arr.length * Math.ceil(Math.log(arr.length)/Math.log(2)));
//Display Code Ends
}
static int lis(int[] arr, int n, Map<Integer, Integer> memo){
if(n==1){
memo.put(1, 1);
return 1;
}
int lisAti;
int lisAtn = 1;
for(int i = 1; i < n; i++){
complexity++;
if(memo.get(i)!=null){
lisAti = memo.get(i);
}else {
lisAti = lis(arr, i, memo);
}
if(arr[i-1] < arr[n-1] && lisAti +1 > lisAtn){
lisAtn = lisAti +1;
}
}
memo.put(n, lisAtn);
return lisAtn;
}
}
Trong khi tôi chạy đoạn mã trên -
Longest Increasing Sub-Sequence with size 6 is -> 10 22 33 50 60 80
And Time Complexity for Array size 9 is just 36
Which is equivalent to O(n Log n) i.e. 9Log(base2)9 is 36.0
Process finished with exit code 0