Làm thế nào để xác định sự gia tăng dài nhất bằng cách sử dụng lập trình động?


Câu trả lời:


404

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 < ivà kiểm tra cả nếu DP[j] + 1 > DP[i]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ừ bestEndtrong một vòng lặp bằng cách sử dụng prev[bestEnd]. Các -1giá trị là một dấu hiệu dừng lại.


OK, bây giờ đến 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 Xcủa tập hợp đầu vào và làm như sau:

  1. Nếu X> phần tử cuối cùng trong S, sau đó nối Xvà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.

  2. Mặt khác, tìm phần tử nhỏ nhất S, >=hơn Xvà 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 - Nsố 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ế, LISchú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 LIScuố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:

  1. 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ị Xcho đến cuối S.

  2. Mặt khác, tìm chỉ mục của phần tử nhỏ nhất S, >=hơn Xvà thay đổi nó thành X. Đây parent[indexX] = S[index - 1].


4
Nó không thành vấn đề. Nếu 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].
Petar Minchev

11
Nhưng đây là câu trả lờ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?
SexyBeast

19
@Coolvogel - Câu trả lời là [2,3,4,5,8]. Đọc kỹ - Smả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.
Petar Minchev

8
Tôi không thường thấy những lời giải thích rõ ràng như vậy. Không chỉ rất dễ hiểu, bởi vì những nghi ngờ đã được xóa trong phần giải thích, nhưng nó cũng giải quyết bất kỳ vấn đề triển khai nào có thể phát sinh. Tuyệt vời.
Boyang

16
geekforgeek.org/ Có lẽ là lời giải thích tốt nhất về điều này tôi đã thấy
eb80

57

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!

Giải pháp đệ quy

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                                                        

Giải pháp lập trình động O (n ^ 2)

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                                                   

Giải pháp lập trình động O (n log n)

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         

19
Mặc dù tôi đánh giá cao nỗ lực ở đây, nhưng mắt tôi đau khi nhìn chằm chằm vào những mã giả đó.
mostruash

94
mostruash - Tôi không chắc ý của bạn là gì. Câu trả lời của tôi không có mã giả; nó có Python.
Sam King

10
Chà, rất có thể anh ta có nghĩa là quy ước đặt tên của các biến và hàm, điều này cũng khiến mắt tôi 'đau'
Adilli Adil

19
Nếu bạn muốn nói đến quy ước đặt tên của tôi, thì tôi chủ yếu làm theo Hướng dẫn về Phong cách Python của Google. Nếu bạn ủng hộ các tên biến ngắn, tôi thích các tên biến mô tả hơn vì chúng làm cho mã dễ hiểu và duy trì hơn.
Sam King

10
Đối với một triển khai thực tế, nó có thể có ý nghĩa để sử dụng 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ể.
Sam King

22

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]
}

@max có, đó là loại văn bản trong câu trả lời với LCS, n ^ 2 ma trận
Salvador Dali

10

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;
}

4

Dưới đây là ba bước đánh giá vấn đề theo quan điểm lập trình động:

  1. Định nghĩa lặp lại: maxLạng (i) == 1 + maxLạng (j) trong đó 0 <j <i và mảng [i]> mảng [j]
  2. Ranh giới tham số lặp lại: có thể có 0 đến i - 1 chuỗi con được truyền dưới dạng tham số
  3. Thứ tự đánh giá: vì nó đang tăng chuỗi con, nó phải được đánh giá từ 0 đến n

Nếu chúng ta lấy một chuỗi ví dụ {0, 8, 2, 3, 7, 9}, tại chỉ mục:

  • [0] chúng tôi sẽ nhận phần sau {0} làm trường hợp cơ bản
  • [1] chúng tôi có 1 phần tiếp theo mới {0, 8}
  • [2] cố gắng đánh giá hai chuỗi mới {0, 8, 2} và {0, 2} bằng cách thêm phần tử tại chỉ mục 2 vào chuỗi con hiện tại - chỉ có một chuỗi là hợp lệ, do đó, chỉ thêm chuỗi thứ ba có thể {0, 2} vào danh sách tham số ...

Đâ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;
}

Tôi nghĩ định nghĩa lặp lại phải là maxLạng (i) = 1 + max (maxLạng (j)) cho 0 <j <i và mảng [i]> mảng [j] thay vì không có max ().
Slothworks

1

Đâ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)))
  }
}

1

Đâ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


0

Đ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 đó.


0

đâ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);
    }    
}

0

Đâ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));

}

0

kiểm tra mã trong java để tăng thứ tự dài nhất với các phần tử mảng

http://ideone.com/Nd2eba

/**
 **    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();
    }
}

0

Đ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


3
Bạn nên bao gồm mã trong câu trả lời của bạn vì liên kết có thể bị hỏng.
NathanOliver

0

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]+" ");
        }
    }

0
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.


0

Đâ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)

0

Giải pháp LIS đơn giản nhất trong C ++ với độ phức tạp thời gian O (nlog (n))

#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


0

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+" ");
        }

    }
}

0

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


Đưa ra câu trả lời sai cho đầu vào: {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15};
ahadcse
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.