Saya memiliki satu set bilangan bulat. Saya ingin menemukan peningkatan berikutnya terpanjang dari set yang menggunakan pemrograman dinamis.
Saya memiliki satu set bilangan bulat. Saya ingin menemukan peningkatan berikutnya terpanjang dari set yang menggunakan pemrograman dinamis.
Jawaban:
OK, saya akan menjelaskan dulu solusi paling sederhana yaitu O (N ^ 2), di mana N adalah ukuran koleksi. Ada juga solusi O (N log N), yang akan saya jelaskan juga. Lihat di sini untuk itu di bagian Algoritma yang efisien.
Saya akan menganggap indeks array dari 0 hingga N - 1. Jadi mari kita definisikan DP[i]
sebagai panjang dari LIS (terpanjang meningkatkan urutan) yang berakhir pada elemen dengan indeks i
. Untuk menghitung DP[i]
kita melihat semua indeks j < i
dan memeriksa apakah DP[j] + 1 > DP[i]
dan array[j] < array[i]
(kita ingin itu meningkat). Jika ini benar, kami dapat memperbarui untuk saat ini DP[i]
. Untuk menemukan global optimal untuk array, Anda dapat mengambil nilai maksimum dari 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];
}
}
Saya menggunakan array prev
untuk nanti bisa menemukan urutan sebenarnya tidak hanya panjangnya. Cukup kembali secara rekursif dari bestEnd
dalam satu lingkaran menggunakan prev[bestEnd]
. The -1
nilai adalah tanda untuk berhenti.
O(N log N)
solusi yang lebih efisien :Membiarkan S[pos]
didefinisikan sebagai bilangan bulat terkecil yang mengakhiri peningkatan panjang urutan pos
. Sekarang beralih melalui setiap integer X
dari set input dan lakukan hal berikut:
Jika X
> elemen terakhir masuk S
, maka tambahkan X
ke akhir S
. Ini penting artinya kami telah menemukan yang terbesar baru LIS
.
Jika tidak, temukan elemen terkecil di S
, yaitu >=
dari X
, dan ubah ke X
. Karena S
diurutkan kapan saja, elemen dapat ditemukan menggunakan pencarian biner di log(N)
.
Total runtime - N
integer dan pencarian biner untuk masing-masingnya - N * log (N) = O (N log N)
Sekarang mari kita lakukan contoh nyata:
Koleksi bilangan bulat:
2 6 3 4 1 2 9 5 8
Langkah:
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
Jadi panjang LIS adalah 5
(ukuran S).
Untuk merekonstruksi yang sebenarnya LIS
kita akan kembali menggunakan array induk. Membiarkan parent[i]
menjadi pendahulu elemen dengan indeks i
di LIS
akhir di elemen dengan indeks i
.
Untuk mempermudah, kita bisa tetap menggunakan array S
, bukan bilangan bulat sebenarnya, tetapi indeks (posisi) mereka di set. Kami tidak menyimpan {1, 2, 4, 5, 8}
, tetapi menyimpan {4, 5, 3, 7, 8}
.
Itu adalah input [4] = 1 , input [5] = 2 , input [3] = 4 , input [7] = 5 , input [8] = 8 .
Jika kami memperbarui array induk dengan benar, LIS yang sebenarnya adalah:
input[S[lastElementOfS]],
input[parent[S[lastElementOfS]]],
input[parent[parent[S[lastElementOfS]]]],
........................................
Sekarang untuk hal yang penting - bagaimana kita memperbarui array induk? Ada dua opsi:
Jika X
> elemen terakhir S
, maka parent[indexX] = indexLastElement
. Ini berarti induk dari elemen terbaru adalah elemen terakhir. Kami hanya melanjutkan X
sampai akhir S
.
Kalau tidak, cari indeks elemen terkecil di S
, yaitu >=
dari X
, dan ubah ke X
. Di sini parent[indexX] = S[index - 1]
.
DP[j] + 1 == DP[i]
itu DP[i]
tidak akan menjadi lebih baik dengan DP[i] = DP[j] + 1
. Kami berusaha mengoptimalkan DP[i]
.
[1,2,5,8]
, 4 muncul sebelum 1 dalam array, bagaimana LIS [1,2,4,5,8]
?
[2,3,4,5,8]
. Baca dengan cermat - S
array DOES NOT
mewakili urutan aktual. Let S[pos] be defined as the smallest integer that ends an increasing sequence of length pos.
Penjelasan Petar Minchev membantu menjernihkan hal-hal bagi saya, tetapi sulit bagi saya untuk menguraikan semua itu, jadi saya membuat implementasi Python dengan nama variabel yang terlalu deskriptif dan banyak komentar. Saya melakukan solusi rekursif naif, solusi O (n ^ 2), dan solusi O (n log n).
Saya harap ini membantu menjernihkan algoritme!
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
. Untuk mendemonstrasikan bagaimana suatu algoritma bekerja dan karakteristik kinerjanya, saya mencoba untuk menjaga hal-hal se primitif mungkin.
Berbicara tentang solusi DP, saya menemukan mengejutkan bahwa tidak ada yang menyebutkan fakta bahwa LIS dapat direduksi menjadi LCS . Yang perlu Anda lakukan adalah mengurutkan salinan dari urutan asli, menghapus semua duplikat dan melakukan LCS mereka. Dalam pseudocode itu adalah:
def LIS(S):
T = sort(S)
T = removeDuplicates(T)
return LCS(S, T)
Dan implementasi penuh ditulis dalam Go. Anda tidak perlu mempertahankan seluruh matriks n ^ 2 DP jika Anda tidak perlu merekonstruksi solusinya.
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]
}
Implementasi C ++ berikut ini juga menyertakan beberapa kode yang membangun peningkatan terpanjang aktual yang sebenarnya menggunakan sebuah array bernama 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;
}
Implementasi tanpa tumpukan hanya membalikkan vektor
#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;
}
Berikut adalah tiga langkah mengevaluasi masalah dari sudut pandang pemrograman dinamis:
Jika kita ambil sebagai contoh urutan {0, 8, 2, 3, 7, 9}, pada indeks:
Berikut kode C ++ 11 yang berfungsi:
#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;
}
Berikut ini adalah implementasi Scala dari algoritma 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)))
}
}
Inilah implementasi JAWA O (n ^ 2) yang lain. Tidak ada rekursi / memoisasi untuk menghasilkan selanjutnya yang sebenarnya. Hanya larik string yang menyimpan LIS aktual di setiap tahap dan larik untuk menyimpan panjang LIS untuk setiap elemen. Sangat mudah. Lihat:
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);
}
}
Kode beraksi: http://ideone.com/sBiOQx
Ini dapat diselesaikan dalam O (n ^ 2) menggunakan Pemrograman Dinamis. Kode python untuk hal yang sama akan seperti: -
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)
Untuk input:5 19 5 81 50 28 29 1 83 23
output akan menjadi:[1, 2, 1, 3, 3, 3, 4, 1, 5, 3]
5
List_index daftar output adalah list_index dari daftar input. Nilai pada list_index dalam daftar output menunjukkan panjang terpanjang meningkat berikutnya untuk list_index itu.
di sini adalah implementasi java O (nlogn)
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);
}
}
Ini adalah implementasi Java dalam O (n ^ 2). Saya hanya tidak menggunakan Pencarian Biner untuk menemukan elemen terkecil dalam S, yaitu> = daripada X. Saya hanya menggunakan untuk loop. Menggunakan Binary Search akan membuat kompleksitas di 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));
}
checkout kode di java untuk penambahan yang paling lama dengan elemen array
/**
** 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();
}
}
Ini dapat diselesaikan dalam O (n ^ 2) menggunakan pemrograman dinamis.
Memproses elemen input secara berurutan dan memelihara daftar tupel untuk setiap elemen. Setiap tuple (A, B), untuk elemen yang akan saya tunjukkan, A = panjang sub-urutan terpanjang yang berakhir pada i dan B = indeks pendahulu daftar [i] dalam sub-urutan terpanjang yang meningkat yang berakhir pada daftar [i ]
Mulai dari elemen 1, daftar tuple untuk elemen 1 adalah [(1,0)] untuk elemen i, pindai daftar 0..i dan temukan daftar elemen [k] sedemikian rupa sehingga daftar [k] <daftar [i] , nilai A untuk elemen i, Ai akan menjadi Ak + 1 dan Bi akan menjadi k. Jika ada beberapa elemen seperti itu, tambahkan mereka ke daftar tupel untuk elemen i.
Pada akhirnya, temukan semua elemen dengan nilai maksimal A (panjang LIS yang berakhir pada elemen) dan mundur menggunakan tupel untuk mendapatkan daftar.
Saya telah membagikan kode yang sama di http://www.edufyme.com/code/?id=66f041e16a60928b05a7e228a89c3799
O (n ^ 2) implementasi java:
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)
walaupun ada cara dimana Anda dapat menyelesaikan ini dalam waktu O (nlogn) (ini diselesaikan dalam waktu O (n ^ 2)) tetapi masih dengan cara ini memberikan pendekatan pemrograman dinamis yang juga baik.
Berikut ini adalah solusi Leetcode saya menggunakan Pencarian Biner: ->
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;
}
OUTPUT:
4
Sub selanjutnya yang Meningkat Terlama (Jawa)
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+" ");
}
}
}
Saya telah mengimplementasikan LIS di java menggunakan Dynamic Programming and Memoization. Seiring dengan kode saya telah melakukan perhitungan kompleksitas yaitu mengapa itu adalah O (n Log (base2) n). Karena saya merasa penjelasan teoretis atau logis itu baik tetapi demonstrasi praktis selalu lebih baik untuk dipahami.
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;
}
}
Sementara saya menjalankan kode di atas -
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