[Python] 最長増加部分列(LIS)問題

ほぼ以下の内容です。

GeeksForGeeks Python program for Longest Incresing Subsequence

最長増加部分列(LIS: Longest Increasing Subsequence)問題

最長増加部分列(LIS: Longest Increasing Subsequence)問題とは、与えられた配列の中で、増加順に並んでいる最も長い部分列を見つける問題です。

  • [10, 22, 9, 33, 21, 50, 41, 60, 80]の時、LIS は[10, 22, 33, 50, 60, 80]、長さ6です。
  • [3, 10, 2, 1, 20]の時、LISは[3, 10, 20]、長さは3です。
  • [3, 2]の時、LISは[3]または[2]、長さは1です。
  • [50, 3, 10, 7, 40, 80]の時、LISは[3, 7, 40, 80]、長さは4 です。

再帰

arr[0..n-1]の配列が与えられた時、arr[i]をLISの最後の要素、L(i)をLISの長さとする。

するとL(i)は再帰的に下のように書くことができる。

L(i) = 1 + max( L(j) )  ただし、0 < j < i かつarr[j] < arr[i]の時。または、
L(i) = 1

## 最大値を保存する。
global maximum

def rec_lis(arr, n):
    # グローバル変数にアクセスする。
    global maximum
    # 終了条件
    if n == 1:
        return 1
    # 要素arr[n-1]で終わるLISの長さ
    max_ending_here = 1

    # arr[0], arr[1] ... arr[n-2]と再帰的に全てのLISを得る。
    for i in range(1, n):
        res = rec_lis(arr, i)
        if arr[i -1] < arr[n - 1] and res + 1 > max_ending_here:
            max_ending_here = res + 1
    # 全体の最大値の更新
    maximum = max(maximum, max_ending_here)

    return max_ending_here

def lis(arr):

    global maximum

    n = len(arr)
    maximum = 1
    rec_lis(arr, n)

    return maximum


arr = [10, 22, 9, 33, 21, 50, 41, 60]
print(lis(arr))

DP

def lis(arr):
    n = len(arr)

    # dp
    lis = [1] * n

    for i in range(1, n):
        for j in range(i):
            if arr[i] > arr[j] and lis[i] < lis[j] + 1:
                lis[i] = lis[j] + 1
    
    maximum = 0
    for i in range(n):
        maximum = max(maximum, lis[i])
    
    return maximum

arr = [10, 22, 9, 33, 21, 50, 41, 60]
print(lis(arr))