문제
- 문제 링크
BOJ 30975 - 약간 모자라지만 착한 친구야
경상국립대와 그 주변에 있는 동네 $N$개를 잇는 $M$개의 도로 정보가 주어진다.
동네별 $P_i$에 유의한 채, 경상국립대에서 시작해 모든 동네를 한 번씩 방문한 뒤 돌아오는 최소 시간을 구해보자.
TL : $1$ sec, ML : $512$ MB
$1 ≤ P_i ≤ N ≤ 14$ $1 ≤ M ≤ 200$ $1 ≤ w_i ≤ 100$ 시작 지점과 도착 지점이 모두 동일한 도로가 두 개 이상 존재할 수 있다.
알고리즘 분류
- 다이나믹 프로그래밍 (dp)
- 비트마스킹 (bitmask)
- 비트필드 위에서의 다이나믹 프로그래밍(dp_bitfield)
- 외판원 순회 문제 (tsp)
풀이
문제의 요구 사항과 제한을 보면 $tsp$ 냄새가 물씬 난다.
$N+1$번 정점까지 생각했을 때 정점의 수는 최대 $15$개가 될 수 있으므로, 방문 상태를 비트로 표현하는 $Bit$ $DP$로 문제를 풀 수 있다.
아래와 같이 상태를 정의해보자.
기저 사례로, 모든 정점을 방문했다면 마지막으로 서있던 정점에서 $N+1$번 정점으로 돌아가는 비용을 반환하자.
$now$에서 $i$로 탐색이 들어가려면 아래의 조건을 충족해야 한다.
상태가 $N*2^N$개, 각 상태를 계산하는 데에 $O(N)$의 시간이 걸리므로 문제를 총 $O(N^2*2^N)$에 해결할 수 있다.
전체 코드
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 32 33 34 35 36 37 38 39 40 | #include<bits/stdc++.h> #define inf 0x3f3f3f3f using namespace std; int p[15], M[16][16], dp[15][1 << 15]; int n, m; int f(int now, int status) { if (status == (1 << (n + 1)) - 1) return M[now][n]; int& cur(dp[now][status]); if (!~cur) { cur = inf; for (int i{}; i <= n; i++) if (!(status & (1 << i)) && M[now][i] < inf && (p[i] == i || status & (1 << p[i]))) cur = min(cur, f(i, status | (1 << i)) + M[now][i]); } return cur; } int main() { cin >> n >> m; for (int i{}; i < n; --p[i++]) cin >> p[i]; memset(M, inf, sizeof M); memset(dp, -1, sizeof dp); while (m--) { int u, v, w; cin >> u >> v >> w; u--, v--; M[u][v] = min(M[u][v], w); } int ans(f(n, 1 << n)); cout << (ans < inf ? ans : -1); } | cs |
comment
끝
'Problem Solving > Baekjoon Online Judge (Platinum)' 카테고리의 다른 글
백준 31222 - 수열과 어렵지 않은 쿼리 (C++) (0) | 2024.08.27 |
---|---|
백준 30976 - 사랑의 큐피드 (C++) (0) | 2023.12.24 |
백준 30943 - Zatopljenje (C++) (1) | 2023.12.11 |
백준 30512 - 조용히 완전히 영원히 (C++) (1) | 2023.11.20 |
백준 30691 - 슈퍼 트리 뽀개기 (C++) (1) | 2023.11.20 |