From febfa6273b372c576db340d9d27ae2c9ff27983d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=98=84=EB=AF=BC=EA=B5=AC?= Date: Mon, 27 Jul 2026 17:57:40 +0900 Subject: [PATCH] =?UTF-8?q?solve:=20[W01]=20SWEA=2026701=20=EB=B8=94?= =?UTF-8?q?=EB=A1=9D=20=EC=A0=9C=EA=B1=B0=20=EA=B2=8C=EC=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...4\352\261\260 \352\262\214\354\236\204.md" | 176 ++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 "studies/week-02/min9u/26071-\353\270\224\353\241\235 \354\240\234\352\261\260 \352\262\214\354\236\204.md" diff --git "a/studies/week-02/min9u/26071-\353\270\224\353\241\235 \354\240\234\352\261\260 \352\262\214\354\236\204.md" "b/studies/week-02/min9u/26071-\353\270\224\353\241\235 \354\240\234\352\261\260 \352\262\214\354\236\204.md" new file mode 100644 index 0000000..5911df7 --- /dev/null +++ "b/studies/week-02/min9u/26071-\353\270\224\353\241\235 \354\240\234\352\261\260 \352\262\214\354\236\204.md" @@ -0,0 +1,176 @@ +# [SWEA 1767] 프로세서 연결하기 + +| 항목 | 내용 | +| :----------- | :--------------------------------------------------------------------------------------------- | +| 🔗 문제 | https://swexpertacademy.com/main/solvingProblem/solvingProblem.do | +| 📚 주제 | DP | +| 🎚️ 난이도 | D4 | +| ⏱️ 소요 시간 | 2시간 30분 | +| 🔁 다시 풀기 | 예 | + +--- + +## 1. 접근 방법 + +처음에 쉽게 아이디어가 생각나지 않아 단순히 완전탐색으로 풀어보았습니다. (주석에 추가) +시간초과로 DP 아이디어를 생각해 풀어보았습니다. + +## 2. 시간 복잡도 + + +**O(T × N³)** + +리스트 앞뒤에 1을 붙여 n+2개 원소로 만든 뒤, dp[i][j] 형태의 구간을 정의합니다. +가능한 구간 (i, j)의 개수는 O(N²)개이고, 각 구간마다 "마지막으로 깨지는 블록 k"를 O(N)개 시도하며 값을 구합니다. +메모이제이션(dp[a][b] != -1 체크)으로 한 번 계산된 구간은 재계산하지 않으므로, 전체 계산량은 **구간 수(O(N²)) × 구간당 전이 비용(O(N)) = O(N³)**입니다. +테스트케이스가 T개이므로 총 시간복잡도는 **O(T × N³)**이며, N≤10, T≤50 제약에서는 최대 약 50 × 10³ = 50,000번 연산 수준이라 충분히 빠릅니다. +(참고: 완전탐색은 O(N!)이었던 것과 비교하면, N=10 기준 3,628,800배 → 1,000배 수준으로 극적으로 줄어든 셈입니다.) + +## 3. 공간 복잡도 + +**O(N²)** + +dp 테이블이 (n+2) × (n+2) 크기의 2차원 배열이므로, 저장 공간은 O(N²)입니다. +추가로 재귀 호출에 따른 콜스택 깊이가 있지만, 이는 최대 구간 크기에 비례하는 O(N) 수준이라 전체 공간복잡도를 지배하지 않습니다. + +## 4. 핵심 풀이 방법 ⭐ + +1. 핵심 아이디어는 **마지막으로 깨지는 블록** 만 고려하면 간단한 DP 문제가 된다는 것입니다. 처음 문제를 직관적으로 보았을 때에는 블록들이 깨지는 순서를 모두 고려해야 할 것 같지만 그렇지 않습니다. +**특정 구간 내**(i,j)에서의 최대 점수를 DP(i,j)라고 정의하고, **마지막으로 깨지는 블록**을 k라고 정의하면 다음과 같이 부분식을 만들 수 있습니다. +DP(i,j) = DP(i,k) + DP(k,j) + arr[i]*arr[j] +마지막에 깨지는 블록이 무엇이 되든 마지막 점수는 arr[i]*arr[j]가 됩니다. + +2. 입력된 array 처음과 끝에 원소 1을 추가해 문제를 더 단순히 만들었습니다. 좋은 아이디어라 다른 문제에서도 응용될 것 같습니다 +3. 마지막으로 깨지는 블록은 cost가 arr[i]*arr[j]가 아니라 arr[k] 것을 주의해야합니다. +## 5. 실제 코드 + +```python +""" +DP를 사용할 수 있는 조건 :문제를 부분문제들로 나눠서 통합할수 있는가 +dp[i][j] = max(dp[i][k] + dp[k][j] + arr[i][j]) +dp[i][i+2] = arr[i]*arr[i+2] +dp[i][i+1] = 0 + +입력받은 리스트의 앞과 뒤에 원소 1을 추가하여 계산이 용이하게 만들었다. +예외조건으로 마지막으로 제거되는 블록은 자기 점수를 반환하는 것을 주의해야한다. + +하향식으로 구현하여 dp[0][n+1]을 최종적으로 반환하는 함수를 구현한다 +""" +T = int(input()) +for tc in range(1, T+1): + n = int(input()) + li = list(map(int ,input().split())) + # 입력받은 리스트의 앞과 뒤에 원소 1을 추가하여 계산이 용이하게 만듬 + li = [1] + li + [1] + n+=2 + + dp = [[-1]*n for _ in range(n)] + + def cost(a,b): + # 이미 계산된 함수값은 배열에서 찾아서 반환 + if dp[a][b] != -1: + return dp[a][b] + # 최소단위의 부분 문제들 + if b-a == 1: + dp[a][b] = 0 + return 0 + if b-a == 2: + dp[a][b] = li[a]*li[b] + return dp[a][b] + + # 부분문제들로 나눈다 + max_score = 0 + for k in range(a+1, b): + # !! 마지막으로 깨지는 블록 예외처리 + if a==0 and b==n-1: + score = cost(a,k) + cost(k,b) + li[k] + else: + score = cost(a,k) + cost(k,b) + li[a]*li[b] + max_score = max(max_score, score) + # dp 배열에 이미 계산한 값을 저장해, 두번이상 같은 함수를 계산하지 않는다 + dp[a][b] = max_score + return max_score + + ans = cost(0,n-1) + # !! 리스트 원소가 하나일 때 예외처리 + if n==3: + ans = li[1] + print(f'#{tc} {ans}') + + +""" +완전탐색으로 구현해보고 시간 초과남 => dp로 전환 +""" +# T = int(input()) +# for tc in range(1, T+1): +# n = int(input()) +# li = list(map(int, input().split())) + +# max_score = 0 +# score = 0 +# def dfs(): +# global max_score, score, li # li는 global로 넣어야할지 말아야할지 헷갈린다 +# for idx, val in enumerate(li): +# if val == 0: +# continue +# l_idx, r_idx = idx,idx +# l_value, r_value = 0,0 +# while True: +# l_idx -= 1 +# if l_idx<0: +# break +# l_value = li[l_idx] +# if l_value>0: +# break +# while True: +# r_idx += 1 +# if r_idx>=len(li): +# break +# r_value = li[r_idx] +# if r_value>0: +# break + +# # 이웃 없음 +# if r_value == 0 and l_value == 0: +# score += val +# max_score = max(max_score, score) +# score -= val +# return + +# # 한쪽만 이웃 +# elif r_value == 0 and l_value: +# score += l_value +# li[idx] = 0 +# dfs() +# li[idx] = val +# score -= l_value + +# elif l_value == 0 and r_value: +# score += r_value +# li[idx] = 0 +# dfs() +# li[idx] = val +# score -= r_value + +# # 둘다 이웃 +# else: +# score += l_value*r_value +# li[idx] = 0 +# dfs() +# score -= l_value*r_value +# li[idx] = val + +# dfs() +# print(f"#{tc} {max_score}") + +``` + +--- + +### 🧠 회고 (선택) + + +DP로 푸는 아이디어가 먼저 떠오르지 않아 완전탐색으로 구현한 후 시간초과가 나왔습니다. +DP로 풀수 있는 문제들(부분문제들도 나눠서 통합)을 더 풀어보고 직관적으로 DP로 풀수있는 아이디어가 나와야 시간을 크게 소요하지 않을 것 같습니다. + +예외처리 코드가 예쁘지 않아서 더 좋은 예외처리 코드는 무엇일지 같이 고민해주시면 좋겠습니다.