Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions studies/week-01/clarityth/1767-프로세서 연결하기.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
<!--
📄 문제 풀이 템플릿
이 파일을 복사해 studies/week-XX/<깃허브ID>/<문제번호>-<문제이름>.md 로 저장하세요.
아래 5요소는 반드시 모두 작성합니다. (접근/시간/공간/핵심/코드)
-->

# [SWEA 1767] 프로세서 연결하기

| 항목 | 내용 |
|:---|:---|
| 🔗 문제 | https://swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AV4suNtaXFEDFAUf |
| 📚 주제 | DFS |
| 🎚️ 난이도 | D4 |
| ⏱️ 소요 시간 | 1시간 30분 |
| 🔁 다시 풀기 | 아니오 |

---

## 1. 접근 방법
<!-- 문제를 어떻게 이해했고, 왜 이 전략을 택했는지. 처음 떠올린 방법과 막혔던 지점도 좋음. -->
결론적으로 DFS와 백트래킹을 활용한 완전 탐색 방식을 적용하여 문제에 접근했습니다.

* **처음 떠올린 방법**
* 직관적인 풀이 방법이 떠오르지 않아, 모든 경우의 수를 확인하는 DFS 및 백트래킹 방식을 선택했습니다.

* **막혔던 지점**
* 여러 로직이 순차적으로 연결되어야 하는 구조로 인해 구현에 어려움을 겪었습니다.
* 코어들의 위치를 배열에 별도로 저장하는 작업
* 저장된 위치를 순회하며 각 방향별 탐색을 진행하는 작업
* 전선이 격자 범위를 벗어날 경우 연결이 가능한지 알려주는 조건 설정
* 유효한 연결에 대해서만 탐색한 경로를 `visited` 배열에 기록한 뒤, 다음 DFS 호출 시 해당 상태를 반영하는 과정
* 이전 최대 연결 코어 갯수와 현재 코어 갯수가 같다면 **최소 전선 길이만** 갱신하는 로직 누락

## 2. 시간 복잡도
<!-- 예: O(N log N) — 정렬 O(N log N) + 순회 O(N). 근거를 함께 적기. -->
<!-- SWEA는 시간 제한이 빡빡한 문제가 많으니 반드시 따져볼 것! -->

**O(N * 5^C)** — (단, C는 가장자리를 제외한 코어의 개수)

각 코어마다 4가지 방향으로 전선을 연결하는 경우와 연결을 포기하는 경우를 합쳐 총 5가지의 선택지가 존재

모든 코어에 대해 재귀 탐색을 진행하므로 상태 공간 트리의 최대 크기는 5^C

각 방향 탐색 시 전선의 배치 가능 여부를 확인하고 경로를 기록하는 내부 while 루프에서 최대 N번의 연산이 수행되므로 전체 시간 복잡도는 O(N * 5^C)

문제의 조건이 7 <= N <= 12, 1 <= C <= 12이므로, 최악의 경우 연산 횟수는 12 * 5^12에 비례(약 29억)
Comment on lines +38 to +46

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

file='studies/week-01/clarityth/1767-프로세서 연결하기.md'

printf 'FILE: %s\n' "$file"
wc -l "$file"
printf '\n--- outline / nearby structure ---\n'
sed -n '1,220p' "$file" | cat -n

Repository: DevJunz/coding-test-study-template

Length of output: 6058


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file='studies/week-01/clarityth/1767-프로세서 연결하기.md'

echo '--- relevant slices ---'
sed -n '1,180p' "$file" | nl -ba | sed -n '1,180p'

Repository: DevJunz/coding-test-study-template

Length of output: 236


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file='studies/week-01/clarityth/1767-프로세서 연결하기.md'

rg -n "시간 복잡도|공간 복잡도|핵심 풀이|실제 코드|def |class |return|while|dfs|backtrack|visited|can_pass|cur_path|wire" "$file"

Repository: DevJunz/coding-test-study-template

Length of output: 1867


상한 가지치기를 넣으세요.
복잡도 계산 자체는 테스트케이스당 O(N·5^C)로 맞지만, 전체는 O(T·N·5^C)입니다. 현재 DFS는 남은 코어로도 max_cores를 넘지 못하는 상태를 자르지 않아, N=12, C=12에서는 Python에서 TLE 위험이 큽니다. 이 pruning을 추가하고, 유망한 방향부터 먼저 탐색하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@studies/week-01/clarityth/1767-프로세서` 연결하기.md around lines 38 - 46, DFS에 남은 코어
수를 활용한 상한 가지치기를 추가해, 현재 연결 코어 수와 남은 코어를 모두 사용해도 max_cores를 넘을 수 없는 상태는 즉시 반환하세요.
탐색 순서는 각 코어에서 연결 가능성이 높은 방향을 먼저 시도하도록 정렬해 유망한 분기를 우선 처리하고, 전체 복잡도 설명은 테스트케이스 수를
반영한 O(T·N·5^C)로 수정하세요.

Source: Path instructions


## 3. 공간 복잡도
<!-- 예: O(N) — 방문 배열 N개. -->

**O(N^2)** — 데이터를 저장하는 2차원 리스트 grid가 N * N의 공간을 차지

DFS 과정에서 사용하는 visited 세트(Set)에는 전선의 좌표가 저장되며, 격자의 대부분을 차지하는 최악의 경우 공간 요구량은 N^2에 비례

재귀 호출로 인한 시스템 콜 스택의 최대 깊이는 코어의 총 개수인 C, C는 N^2 이내의 값이므로 최종 공간 복잡도는 가장 높은 차수인 O(N^2)로 결정

## 4. 핵심 풀이 방법 ⭐
<!-- 이 문제의 결정적 아이디어 / 자주 하는 실수 / 알아야 할 개념. 가장 중요한 항목! -->
* **완전 탐색(DFS) 및 백트래킹 적용**
* 가장자리를 제외한 코어들의 위치를 배열에 저장합니다.
* 각 코어에 대해 전선을 아예 연결하지 않는 경우를 먼저 재귀 호출하고, 이후 4가지 방향(상하좌우)으로 전선을 연결하는 경우를 순차적으로 탐색합니다.
* **직선 경로 검증 및 상태 관리**
* 선택한 방향으로 격자의 경계를 벗어날 때까지 좌표를 이동시키며, 다른 코어나 이미 설치된 전선을 만나면 해당 방향의 탐색을 중단합니다.
* 끝까지 도달하여 연결이 가능한 경우, 이동한 경로의 좌표들을 `visited` 에 기록합니다. 다음 탐색이 끝난 후에는 해당 좌표들을 다시 제거하여 상태를 원상 복구합니다.
* **정답 갱신 우선순위**
* 모든 코어의 탐색을 완료한 기저 조건에서, 연결된 코어 수가 기존의 최대 코어 수보다 크면 코어 수와 전선 길이를 모두 갱신합니다.
* 연결된 코어 수가 기존 최댓값과 같다면, 전선의 길이를 비교하여 더 짧은 경우에만 갱신합니다.


## 5. 실제 코드
<!-- 통과한 코드. 언어 태그를 정확히 표기하세요. -->

```python
T = int(input())

for test_case in range(1, T+1) :
N = int(input())
grid = [list(map(int, input().split())) for _ in range(N)]

max_cores = 0 # 최대 연결 코어 갯수 저장
min_wire_len = float('inf') # 최소 전선 길이(정답) 저장

core_pos = [] # 코어들의 위치를 저장

# 가장 자리 코어 제외
for i in range(1, N-1):
for j in range(1, N-1):
if grid[i][j] == 1:
core_pos.append((i, j)) # (y,x)

dx = [0, 0, -1, 1]
dy = [-1, 1, 0, 0]

def dfs(idx, visited, tot_cores, tot_wire_len):
global max_cores, min_wire_len

# 기저 조건
if idx == len(core_pos):
# 현재 저장된 최대 연결 코어 수보다 크다면 변수 업데이트, 전선 길이 갱신
if tot_cores > max_cores:
max_cores = tot_cores
min_wire_len = tot_wire_len

# 최대 연결 코어 갯수와 같다면 최소 전선 길이만 갱신
elif tot_cores == max_cores:
min_wire_len = min(min_wire_len, tot_wire_len)

return

# 해당 코어를 선택하지 않는 경우
dfs(idx+1, visited, tot_cores, tot_wire_len)

# 해당 코어로부터 4방향 중 가능한 방향을 선택하는 경우
for i in range(4):
ny, nx = core_pos[idx][0], core_pos[idx][1] # 다음 위치 저장
can_pass = False # 이동 가능 여부 저장
cur_path = [] # 이동 경로를 저장
wire = 0 # 전선 길이 저장

while True:
# 다음 위치 이동
ny += dy[i]
nx += dx[i]

if 0 <= ny < N and 0 <= nx < N:
# 다른 코어가 위치하고 있거나, 이전에 방문했다면(교차) 진행 불가
if grid[ny][nx] == 1 or (ny, nx) in visited:
break

# 이동 경로에 기록
cur_path.append((ny, nx))

# 범위 밖까지 도달했다면 도달 가능 변수 업데이트
else:
can_pass = True
break

wire += 1 # 전선 길이 증가

# 백트래킹
if can_pass:
# 이동 경로를 visited에 기록
for path_y, path_x in cur_path:
visited.add((path_y, path_x))

# 해당 경로를 선택한 채, 다음 dfs 진행
dfs(idx+1, visited, tot_cores+1, tot_wire_len+wire)

# 원상 복구
for path_y, path_x in cur_path:
visited.remove((path_y, path_x))
Comment on lines +116 to +151

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

can_pass 플래그를 반환값 기반 연결 검사로 단순화할 수 있습니다.

현재 로직은 can_pass, cur_path, wire를 별도로 관리해 상태 흐름이 길어집니다. 연결 가능한 경로를 반환하고, 실패 시 None을 반환하는 helper로 분리하면 wire == len(path)도 자연스럽게 보장됩니다.

개선 예시
+    def find_path(y, x, direction, visited):
+        path = []
+        while True:
+            y += dy[direction]
+            x += dx[direction]
+
+            if not (0 <= y < N and 0 <= x < N):
+                return path
+            if grid[y][x] == 1 or (y, x) in visited:
+                return None
+
+            path.append((y, x))
+
     def dfs(idx, visited, tot_cores, tot_wire_len):
         ...
         for i in range(4):
-            ny, nx = core_pos[idx][0], core_pos[idx][1]
-            can_pass = False
-            cur_path = []
-            wire = 0
-
-            while True:
-                ...
+            cur_path = find_path(*core_pos[idx], i, visited)
+            if cur_path is None:
+                continue
 
-            if can_pass:
-                ...
-                dfs(idx+1, visited, tot_cores+1, tot_wire_len+wire)
-                ...
+            for path_y, path_x in cur_path:
+                visited.add((path_y, path_x))
+            dfs(idx + 1, visited, tot_cores + 1,
+                tot_wire_len + len(cur_path))
+            for path_y, path_x in cur_path:
+                visited.remove((path_y, path_x))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
can_pass = False # 이동 가능 여부 저장
cur_path = [] # 이동 경로를 저장
wire = 0 # 전선 길이 저장
while True:
# 다음 위치 이동
ny += dy[i]
nx += dx[i]
if 0 <= ny < N and 0 <= nx < N:
# 다른 코어가 위치하고 있거나, 이전에 방문했다면(교차) 진행 불가
if grid[ny][nx] == 1 or (ny, nx) in visited:
break
# 이동 경로에 기록
cur_path.append((ny, nx))
# 범위 밖까지 도달했다면 도달 가능 변수 업데이트
else:
can_pass = True
break
wire += 1 # 전선 길이 증가
# 백트래킹
if can_pass:
# 이동 경로를 visited에 기록
for path_y, path_x in cur_path:
visited.add((path_y, path_x))
# 해당 경로를 선택한 채, 다음 dfs 진행
dfs(idx+1, visited, tot_cores+1, tot_wire_len+wire)
# 원상 복구
for path_y, path_x in cur_path:
visited.remove((path_y, path_x))
def find_path(y, x, direction, visited):
path = []
while True:
y += dy[direction]
x += dx[direction]
if not (0 <= y < N and 0 <= x < N):
return path
if grid[y][x] == 1 or (y, x) in visited:
return None
path.append((y, x))
cur_path = find_path(*core_pos[idx], i, visited)
if cur_path is None:
continue
# 이동 경로를 visited에 기록
for path_y, path_x in cur_path:
visited.add((path_y, path_x))
# 해당 경로를 선택한 채, 다음 dfs 진행
dfs(idx + 1, visited, tot_cores + 1, tot_wire_len + len(cur_path))
# 원상 복구
for path_y, path_x in cur_path:
visited.remove((path_y, path_x))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@studies/week-01/clarityth/1767-프로세서` 연결하기.md around lines 116 - 151, Refactor
the connection-checking loop to use a helper that returns the traversed path
when the wire reaches outside the grid and returns None when blocked by a core
or visited cell. In the surrounding backtracking logic, replace can_pass,
cur_path, and wire state handling with the helper result, using the returned
path length for the wire length while preserving visited marking, DFS recursion,
and rollback.

Source: Path instructions


dfs(0, set(), 0, 0)
print(f"#{test_case} {min_wire_len}")

```

---

### 🧠 회고 (선택)
<!-- 배운 점, 비슷한 유형에서 재사용할 아이디어, 리뷰어에게 묻고 싶은 점 -->
Loading