구간 DP(다루마 떨어뜨림)의 설명 기사를 개인적으로 씹어 보았다
(이해에 잘못이 있을지도 모르지만...)
이해할 수 없었던 부분은 이하의 2 점
1. 왜 오른쪽 해방?
2.2 케이스로 나누고 있지만, 다른 케이스는 없는 것인가?
1. 왜 오른쪽 해방?
· 단지 index가 0 시작이기 때문에
・0<=l,r
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()
Reference
이 문제에 관하여(구간 DP(다루마 떨어뜨림)의 설명 기사를 개인적으로 씹어 보았다), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/mizoono/items/18cda7b6333bf35fb9ad텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)