diff --git "a/studies/week-01/clarityth/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/clarityth/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..83a6105 --- /dev/null +++ "b/studies/week-01/clarityth/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,161 @@ + + +# [SWEA 1767] ν”„λ‘œμ„Έμ„œ μ—°κ²°ν•˜κΈ° + +| ν•­λͺ© | λ‚΄μš© | +|:---|:---| +| πŸ”— 문제 | https://swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AV4suNtaXFEDFAUf | +| πŸ“š 주제 | DFS | +| 🎚️ λ‚œμ΄λ„ | D4 | +| ⏱️ μ†Œμš” μ‹œκ°„ | 1μ‹œκ°„ 30λΆ„ | +| πŸ” λ‹€μ‹œ ν’€κΈ° | μ•„λ‹ˆμ˜€ | + +--- + +## 1. μ ‘κ·Ό 방법 + +결둠적으둜 DFS와 λ°±νŠΈλž˜ν‚Ήμ„ ν™œμš©ν•œ μ™„μ „ 탐색 방식을 μ μš©ν•˜μ—¬ λ¬Έμ œμ— μ ‘κ·Όν–ˆμŠ΅λ‹ˆλ‹€. + +* **처음 λ– μ˜¬λ¦° 방법** + * 직관적인 풀이 방법이 λ– μ˜€λ₯΄μ§€ μ•Šμ•„, λͺ¨λ“  경우의 수λ₯Ό ν™•μΈν•˜λŠ” DFS 및 λ°±νŠΈλž˜ν‚Ή 방식을 μ„ νƒν–ˆμŠ΅λ‹ˆλ‹€. + +* **λ§‰ν˜”λ˜ 지점** + * μ—¬λŸ¬ 둜직이 순차적으둜 μ—°κ²°λ˜μ–΄μ•Ό ν•˜λŠ” ꡬ쑰둜 인해 κ΅¬ν˜„μ— 어렀움을 κ²ͺμ—ˆμŠ΅λ‹ˆλ‹€. + * μ½”μ–΄λ“€μ˜ μœ„μΉ˜λ₯Ό 배열에 λ³„λ„λ‘œ μ €μž₯ν•˜λŠ” μž‘μ—… + * μ €μž₯된 μœ„μΉ˜λ₯Ό μˆœνšŒν•˜λ©° 각 λ°©ν–₯별 탐색을 μ§„ν–‰ν•˜λŠ” μž‘μ—… + * 전선이 격자 λ²”μœ„λ₯Ό λ²—μ–΄λ‚  경우 연결이 κ°€λŠ₯ν•œμ§€ μ•Œλ €μ£ΌλŠ” 쑰건 μ„€μ • + * μœ νš¨ν•œ 연결에 λŒ€ν•΄μ„œλ§Œ νƒμƒ‰ν•œ 경둜λ₯Ό `visited` 배열에 κΈ°λ‘ν•œ λ’€, λ‹€μŒ DFS 호좜 μ‹œ ν•΄λ‹Ή μƒνƒœλ₯Ό λ°˜μ˜ν•˜λŠ” κ³Όμ • + * 이전 μ΅œλŒ€ μ—°κ²° μ½”μ–΄ κ°―μˆ˜μ™€ ν˜„μž¬ μ½”μ–΄ κ°―μˆ˜κ°€ κ°™λ‹€λ©΄ **μ΅œμ†Œ μ „μ„  길이만** κ°±μ‹ ν•˜λŠ” 둜직 λˆ„λ½ + +## 2. μ‹œκ°„ λ³΅μž‘λ„ + + + +**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μ–΅) + +## 3. 곡간 λ³΅μž‘λ„ + + +**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)) + + dfs(0, set(), 0, 0) + print(f"#{test_case} {min_wire_len}") + +``` + +--- + +### 🧠 회고 (선택) +