-
Notifications
You must be signed in to change notification settings - Fork 0
solve: [W02] SWEA 26701 블록 제거 게임 #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| # [SWEA 1767] 프로세서 연결하기 | ||
|
|
||
| | 항목 | 내용 | | ||
| | :----------- | :--------------------------------------------------------------------------------------------- | | ||
| | 🔗 문제 | https://swexpertacademy.com/main/solvingProblem/solvingProblem.do | | ||
| | 📚 주제 | DP | | ||
| | 🎚️ 난이도 | D4 | | ||
| | ⏱️ 소요 시간 | 2시간 30분 | | ||
| | 🔁 다시 풀기 | 예 | | ||
|
|
||
| --- | ||
|
|
||
| ## 1. 접근 방법 | ||
| <!-- 문제를 어떻게 이해했고, 왜 이 전략을 택했는지. 처음 떠올린 방법과 막혔던 지점도 좋음. --> | ||
| 처음에 쉽게 아이디어가 생각나지 않아 단순히 완전탐색으로 풀어보았습니다. (주석에 추가) | ||
| 시간초과로 DP 아이디어를 생각해 풀어보았습니다. | ||
|
|
||
| ## 2. 시간 복잡도 | ||
| <!-- 예: O(N log N) — 정렬 O(N log N) + 순회 O(N). 근거를 함께 적기. --> | ||
| <!-- SWEA는 시간 제한이 빡빡한 문제가 많으니 반드시 따져볼 것! --> | ||
| **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³)**입니다. | ||
|
Comment on lines
+23
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Markdown lint 경고를 수정해주세요.
Also applies to: 45-45 🧰 Tools🪛 markdownlint-cli2 (0.23.0)[warning] 23-23: Reference links and images should use a label that is defined (MD052, reference-links-images) [warning] 25-25: Reference links and images should use a label that is defined (MD052, reference-links-images) 🤖 Prompt for AI AgentsSources: Path instructions, Linters/SAST tools |
||
| 테스트케이스가 T개이므로 총 시간복잡도는 **O(T × N³)**이며, N≤10, T≤50 제약에서는 최대 약 50 × 10³ = 50,000번 연산 수준이라 충분히 빠릅니다. | ||
| (참고: 완전탐색은 O(N!)이었던 것과 비교하면, N=10 기준 3,628,800배 → 1,000배 수준으로 극적으로 줄어든 셈입니다.) | ||
|
Comment on lines
+21
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win 복잡도는 맞지만 경계값을 추가한 크기는
🧰 Tools🪛 markdownlint-cli2 (0.23.0)[warning] 23-23: Reference links and images should use a label that is defined (MD052, reference-links-images) [warning] 25-25: Reference links and images should use a label that is defined (MD052, reference-links-images) 🤖 Prompt for AI AgentsSource: Path instructions |
||
|
|
||
| ## 3. 공간 복잡도 | ||
| <!-- 예: O(N) — 방문 배열 N개. --> | ||
| **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] 것을 주의해야합니다. | ||
|
Comment on lines
+38
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 점화식 설명을 실제 코드의 두 경우로 분리해주세요. 현재 설명은
이 부분은 독자가 설명만 보고 구현할 때 오답을 만들 수 있습니다. 수정 예시- dp[i][j] = max(dp[i][k] + dp[k][j] + arr[i][j])
+ cost(a, b) = max(cost(a, k) + cost(k, b) + li[a] * li[b])
+ answer = max(cost(0, k) + cost(k, n - 1) + li[k])Also applies to: 48-57 🤖 Prompt for AI AgentsSource: Path instructions |
||
| ## 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로 풀수있는 아이디어가 나와야 시간을 크게 소요하지 않을 것 같습니다. | ||
|
|
||
| 예외처리 코드가 예쁘지 않아서 더 좋은 예외처리 코드는 무엇일지 같이 고민해주시면 좋겠습니다. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
문제 식별자와 풀이 내용이 일치하지 않습니다.
PR 목적과 구현은 블록 제거 게임인데 제목은
SWEA 1767 프로세서 연결하기로 되어 있습니다. 제목을 실제 문제 식별자로 수정하고, 문제 링크도 해당 문제의 직접 링크로 교체해주세요.🤖 Prompt for AI Agents
Source: Path instructions