일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 비상계엄
- 내란수괴 윤석열
- BFS
- 백준
- 내란죄
- Prim
- dfs
- 구조론
- ccw
- 국민의 힘 뿌리
- 오블완
- 알고리즘
- 파비우스 전략
- 이분 탐색
- 티스토리챌린지
- 내란수괴
- 윤석열 내란수괴
- 프림
- 다익스트라
- 유니온 파인드
- 투 포인터
- 왈왈왈
- DP
- 윤석열
- union find
- 분할정복
- 재귀함수
- dfs 백트래킹
- LCA
- Python
- Today
- Total
Toolofv 님의 블로그
[Python] 백준 - 1311 할 일 정하기 1 본문
문제
N명의 사람과 N개의 일이 있다. 각 사람은 일을 하나 담당해야 하고, 각 일을 담당하는 사람은 한 명 이어야 한다. 또한, 모든 사람은 모든 일을 할 능력이 있다.
사람은 1번부터 N번까지 번호가 매겨져 있으며, 일도 1번부터 N번까지 번호가 매겨져 있다.
Dij를 i번 사람이 j번 일을 할 때 필요한 비용이라고 했을 때, 모든 일을 하는데 필요한 비용의 최솟값을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 사람과 일의 수 N (1 ≤ N ≤ 20)이 주어진다. 둘째 줄부터 N개의 줄에는 D의 내용이 주어진다. 비용은 10,000보다 작거나 같은 자연수이다.
출력
모든 일을 하는데 필요한 비용의 최솟값을 출력한다.
문제해결방법 - (서칭 참조)
1. 비트마스크를 이용한 dfs탐색으로 재귀함수 끝부분부터 값이 반영되어 최소값을 출력한다.
비트마스크는 이진수 배열을 마치 dfs의 방문리스트처럼 이용한다.
비트마스크의 장점.
1) $O(1)$의 연산속도를 가진다.
2) 적은 메모리를 사용한다.
3) 구현된 코드가 간소함.
컴퓨터의 이진수는 부호와 숫자가 표현되는데, CPU가 데이터를 처리하는 한 번의 단위가 32bit냐, 64bit냐에 따라 이진수가 채워진다. 표현상 이 문제에서는 00000, 00001, 5자릿수까지만 표현하기로 한다.
2. (1 << i), (1 >> i) 는 비트의 자릿수를 i에 따라 <<, >>의 방향으로 밀어낸다. 이진수가 밀어지면 결국 아래의 패턴이 된다.
1) A << i 식은 A * $2^{i}$과 같다.
00001 이 있고, A = 1, i = 2라면 00100를 출력하고, 00100는 십진수로 4가 된다. (1 * $2^{2}$ = 4)
2) A >> i 식은 A / $2^{i}$과 같다.
00100 이 있고, A = 4, i = 2라면 00001을 출력하고, 00001은 십진수로 1이 된다. (1 // $2^{2}$ = 1)
3. 위 문제처럼 n = 3 일 때, 00111이 되면 모든 사람이 일을 부여받은 것이다. 이제 dfs로 경우의 수마다 선택하게 되면서 dp[idx][bit]에 선택한 비용의 일을 더한 값 중 최소값이 저장된다. idx가 맨 끝에서부터 거슬러서 올라갈 때 각 분기의 값 중 최소값을 추려서 다시 위로 가져가는 개념이다.(토너먼트를 거쳐서 dp[0][0]에 우승자가 모인다.)
십진수 | 이진수 | 십진수 | 이진수 | 십진수 | 이진수 | 십진수 | 이진수 |
1 | 00001 | 2 | 00010 | 3 | 00011 | 4 | 00100 |
5 | 00101 | 6 | 00110 | 7 | 00111 | 8 | 01000 |
1) A & B 는 AND연산으로, 대응하는 두 비트가 모두 0, 1일 때, 1을 반환한다. (0이 나오면 지금 도는 비트를 방문하지 않았다는 말임.)
-> 00101 & 00010 = 00000 (0), 00111 & 00010 = 00010 (2)
2) A | B 는 OR연산으로, 대응하는 두 비트가 다를 경우, 1을 반환한다. (결국 방문의 합이 되어, 방문을 기록할 수 있음.)
-> 00110 | 00001 = 00111(7), 00101 | 00010 = 00111(7)
4. for문에 따라 깊이우선탐색으로 각 경우의 수를 실행하게 되고, 마지막에 dfs(0, 0)에서 각 분기에서 얻은 값들의 최소값을 반영하여 dp[0][0]에 저장하게 된다.
5. 최소값을 출력한다.
- 코드
import sys
input = sys.stdin.readline
'''
A << B 식은 A * 2의 B제곱과 같다.
A >> B 식은 A / 2의 B제곱과 같다.
00001 = 1 00010 = 2 00011 = 3 00100 = 4 00101 = 5 00110 = 6 00111 = 7
'''
n = int(input())
d = [list(map(int, input().split())) for _ in range(n)]
v = [[-1 for _ in range(1<<n)] for _ in range(n)]
def dfs(idx, bit):
if idx == n:
return 0
if v[idx][bit] != -1:
return v[idx][bit]
ans = sys.maxsize
for i in range(n):
if (bit & (1 << i)) == 0:
res = dfs(idx+1, (bit | (1 << i)))
ans = min(ans, res + d[idx][i])
v[idx][bit] = ans
return v[idx][bit]
print(dfs(0, 0))
'Algorithm' 카테고리의 다른 글
[Python] Leet code 3. 중복 문자 없는 가장 긴 부분 문자열 (Longest Substring WIthout Repeating Characters) (0) | 2024.08.13 |
---|---|
[Python] 백준 - 1086 박성원 (0) | 2024.08.03 |
[Python] 백준 - 7869 두 원 (0) | 2024.07.29 |
[Python] 백준 - 20149 선분 교차 3 (0) | 2024.07.25 |
[Python] 백준 - 6549 히스토그램에서 가장 큰 직사각형 (2) | 2024.07.23 |