diff --git "a/studies/week-01/min9u/1767-\355\224\204\353\241\234\354\204\270\354\204\234\354\227\260\352\262\260\355\225\230\352\270\260.md" "b/studies/week-01/min9u/1767-\355\224\204\353\241\234\354\204\270\354\204\234\354\227\260\352\262\260\355\225\230\352\270\260.md" new file mode 100644 index 0000000..01626f8 --- /dev/null +++ "b/studies/week-01/min9u/1767-\355\224\204\353\241\234\354\204\270\354\204\234\354\227\260\352\262\260\355\225\230\352\270\260.md" @@ -0,0 +1,163 @@ +# [SWEA 1767] 프로세서 연결하기 + +| 항목 | 내용 | +| :----------- | :--------------------------------------------------------------------------------------------- | +| 🔗 문제 | https://swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AV4suNtaXFEDFAUf& | +| 📚 주제 | 완전탐색, 백트래킹 | +| 🎚️ 난이도 | D4 | +| ⏱️ 소요 시간 | 1시간 30분 | +| 🔁 다시 풀기 | 예 | + +--- + +## 1. 접근 방법 + +N×N 격자에서 값이 1인 칸이 '코어'이며, 이 중 가장자리(0행/0열/N-1행/N-1열)에 붙어 있는 코어는 이미 전원과 연결된 것으로 간주해 탐색 대상에서 제외한다. 나머지 코어들을 리스트로 모은 뒤, 각 코어마다 + +- 상 / 하 / 좌 / 우 네 방향 중 전선을 그을 수 있는(경로상에 다른 코어·전선이 없는) 방향으로 연결을 시도 +- 혹은 아무 방향으로도 연결하지 않음 + +의 선택지를 두고 DFS(백트래킹)로 모든 경우를 탐색한다. 방향을 선택해 전선을 그으면 해당 경로를 임시로 1로 채워 이후 탐색에서 장애물로 작동하게 하고, 재귀에서 돌아올 때 다시 0으로 복원(백트래킹)한다. + +모든 코어에 대한 선택이 끝나면(리스트 끝에 도달하면) 그 순간의 "연결된 코어 수(core)"와 "전선 길이 합(score)"을 이용해 전역 최적값을 갱신한다. 이때 목표는 **연결된 코어 수를 최대화**하는 것이 1순위, 그 중에서 **전선 길이 합을 최소화**하는 것이 2순위임에 유의해야 한다. + +## 2. 시간 복잡도 + +**O(5^K × N)** — K는 가장자리가 아닌 코어의 개수(문제 제약상 N ≤ 12이므로 K도 그리 크지 않음), 5는 각 코어마다 "상/하/좌/우로 연결" 4가지 + "연결 안 함" 1가지의 선택지 수. 각 선택마다 해당 방향으로 장애물이 있는지 확인하고 그 경로를 채우고/비우는 데 O(N)이 걸리므로 여기에 N을 곱한다. 최악의 경우 5^K가 매우 커 보이지만, 실제로는 이미 막힌 방향은 가지치기되므로 체감 시간은 이보다 훨씬 짧다. + +## 3. 공간 복잡도 + +**O(N²)** — 코어/전선 상태를 저장하는 tile 2차원 배열이 N×N 크기이며, 코어 좌표 리스트(cores)는 최대 N² 개까지 저장될 수 있으나 실제로는 그보다 훨씬 작다. 재귀 호출 스택의 깊이는 O(K) 수준으로 N²에 비해 무시할 만하다. + +## 4. 핵심 풀이 방법 ⭐ + +- **"최대한 많이 연결"이 최우선 조건**이라는 점이 핵심. 즉 모든 코어가 다 연결되지 않아도 되고, 연결된 코어 수가 최대인 경우들 중에서만 전선 길이의 최솟값을 비교해야 한다. (문제 조건을 꼼꼼히 읽지 않으면 "모든 코어를 반드시 연결해야 한다"고 착각하기 쉬움 — 실제로 헤맸던 지점.) +- 각 코어에서 방향을 정할 때, 단순히 "빈 칸인지"가 아니라 **경로 전체에 코어나 다른 전선이 없는지**를 끝까지 확인해야 한다(중간에 하나라도 걸리면 그 방향은 불가능). +- 전선을 그은 뒤 탐색이 끝나면 반드시 원상복구(백트래킹)해야 다른 코어의 방향 선택에 영향을 주지 않는다. `tile` 배열을 실제로 채우고 비우는 방식으로 "장애물 검사"를 재사용한 것이 포인트. +- 전역 변수(`idx, score, min_score, core, max_core`)로 상태를 관리하며, 매 DFS 호출 시작 지점에서 "지금까지 연결된 코어 수 = 지금까지의 최댓값"이면 점수 갱신, "지금까지 연결된 코어 수 > 지금까지의 최댓값"이면 최댓값과 최소 점수를 함께 갱신하는 방식으로 1순위(최대 연결 수)·2순위(최소 전선 길이) 조건을 동시에 처리한다. +- 5가지 선택지(4방향 + 미연결) 모두를 매번 시도해야 하는 완전탐색이므로, 가능한 방향이 아닐 경우 조기에 걸러내는 것이 성능에 중요하다. + +## 5. 실제 코드 + +```python +''' +헤맸던 포인트 : "최대한 많은 코어"에 전원을 연결하였을 경우, 전설 길이의 합이 최소가 되는 값을 구하라 +코어가 다 연결되지 않는 경우를 생각하지 않았음. 문제 조건을 더 자세히 살펴볼것 +''' +T = int(input()) +for tc in range(T): + n = int(input()) + tile = [list(map(int, input().split())) for _ in range(n)] + + cores = [] + for i in range(n): + for j in range(n): + if tile[i][j] == 1: + cores.append((i,j)) + + idx = 0 + score = 0 + min_score = float('inf') + core = 0 # 총 전원이 연결된 코어 개수 + max_core = 0 # 지금까지 가장 많이 연결된 코어 개수 + + + def dfs(): + global idx, score, min_score, max_core, core + if core==max_core: + min_score = min(min_score, score) + if core>max_core: + min_score = score + max_core = core + if idx == len(cores): + return + y,x = cores[idx] + # 상하좌우 각 방향에서 연결이 가능한지 확인 후 dfs + # 상 + go_up = True + for i in range(0,y): + if tile[i][x] == 1: + go_up = False + break + if go_up: + for i in range(0,y): + tile[i][x] = 1 + idx += 1 + score += y + core+=1 + dfs() + core-=1 + score -= y + idx -= 1 + for i in range(0,y): + tile[i][x] = 0 + # 하 + go_down = True + for i in range(y+1,n): + if tile[i][x] == 1: + go_down = False + break + if go_down: + for i in range(y+1,n): + tile[i][x] = 1 + idx+=1 + score+=n-y-1 + core+=1 + dfs() + core-=1 + score-=n-y-1 + idx-=1 + for i in range(y+1,n): + tile[i][x] = 0 + #좌 + go_left = True + for j in range(0,x): + if tile[y][j] == 1: + go_left = False + break + if go_left: + for j in range(0,x): + tile[y][j] = 1 + idx+=1 + score+=x + core+=1 + dfs() + core-=1 + score-=x + idx-=1 + for j in range(0,x): + tile[y][j] = 0 + #우 + go_right = True + for j in range(x+1,n): + if tile[y][j] == 1: + go_right = False + break + if go_right: + for j in range(x+1,n): + tile[y][j] = 1 + idx+=1 + score+=n-x-1 + core+=1 + dfs() + core-=1 + score-=n-x-1 + idx-=1 + for j in range(x+1,n): + tile[y][j] = 0 + # 연결하지 않음 + idx+=1 + dfs() + idx-=1 + return + + dfs() + print(f'#{tc+1} {min_score}') +``` + +--- + +### 🧠 회고 (선택) + +- 문제의 핵심 조건(코어가 전원에 가장 많이 연결될 경우)을 잘못 오해하여 코어에 모든 전원이 연결될 경우로 문제를 풀었음. 문제의 조건을 문제풀이 전에 잘 정리하는 습관을 길러야겠다. +