구간 DP(다루마 떨어뜨림)의 설명 기사를 개인적으로 씹어 보았다

구간 DP의 해설 기사에서( 참고 ), 개인적으로 이해할 수 없는 개소가 있었으므로, 개인적으로 씹어 설명해 본다.
(이해에 잘못이 있을지도 모르지만...)

이해할 수 없었던 부분은 이하의 2 점

1. 왜 오른쪽 해방?
2.2 케이스로 나누고 있지만, 다른 케이스는 없는 것인가?

1. 왜 오른쪽 해방?



· 단지 index가 0 시작이기 때문에
・0<=l,r※AOJ로 TLE 하지만, 그것 같은 소스를 최하부에 붙여 둔다.

dp[l][r] := 구간[l , r]에서 제거할 수 있는 블록 수

・구간 분할점(상기 참고 기사에서는 mid)를 그대로 사용하여 재귀 표현을 하고 싶으니까
양측이 닫힌 표현으로 한 경우(즉 구간[l, r]), 이하의 나누는 방법이 된다.

[l,mid] , [mid+1 , r]로 구분

2.2 케이스로 나누고 있지만, 다른 케이스는 없는 것인가?



참고 기사에서는 이하와 같이 케이스 분할을 하고 있었다.

1. l 블록과 r - 1 블록이 쌍이 될 수 있습니다.

2. [l,mid) , [mid , r)로 구분

1. 케이스는 양쪽 끝에 끼워진 부분이 모두 사라지고 더 남은 양단이 사라지는 케이스. Puyo Puyo의 사슬처럼.
2. 케이스는 2분할하여 각 파트마다 최대 제거수를 구하는 케이스

자신의 의문은, 그 밖에 케이스는 없는 것인가? 라는 것. 두 케이스로 MEC가 되는 이유를 몰랐다.
예를 들어, 양단에 끼워져 있는 개소가 2개를 남기고 사라지고, 더 남은 2개가 양단과 함께 사라지는 케이스는 어떨까? (케이스 A)
양단에 끼워져 있는 개소가 1개를 남겨 사라지고, 한층 더 남은 1개가 양단의 어느 쪽인가와 함께 사라지는 케이스는 어떨까? (케이스 B)

그림에 써 보면 2개의 케이스에 포함되는 것을 알 수 있었다.
(2 케이스에 들어간다는 증명이 생긴 것은 아니다)

케이스 A


케이스 B
import sys

sys.setrecursionlimit(250000)
input = sys.stdin.readline
dp = []
w =[]

def rec(l,r):
    #既に探索済の場合はその値を返す
    if dp[l][r] >= 0:
        return dp[l][r]

    #単一ケースは落とせないので0
    if l == r:
        dp[l][r] = 0
        return 0

    #連続している箇所の場合
    if l + 1 == r:
        if abs(w[l] - w[r]) <= 1:
            dp[l][r] = 2
            return 2
        else:
            dp[l][r] = 0
            return 0

    res = 0

    #Case1 両端に挟まれている箇所がすべて消えて、さらに残った両端が消える
    if abs(w[l] - w[r]) <= 1 and rec(l+1, r-1) == (r - 1) - (l + 1) + 1:
        res =  (r - 1) - (l + 1) + 1 + 2
    else: #Case2 両端に挟まれている箇所が消えない
        for mid in range(l,r):
            res = max(res, rec(l,mid)+rec(mid+1,r))

    dp[l][r]=res
    return res
def main():
    global w
    global dp
    n_list=[]
    w_list=[]

    while True:
        n = int(input())
        if n == 0 :
            break
        w_tmp = list(map(int, input().split()))
        n_list.append(n)
        w_list.append(w_tmp)

    for i in range(len(n_list)):
        dp = [[-1] * n_list[i] for j in range(n_list[i])]
        w = w_list[i]
        print(rec(0,n_list[i]-1))
main()



좋은 웹페이지 즐겨찾기