[BAEKJOON] 2001. 보석 줍기
Posted on
문제 :
n(1 ≤ n ≤ 100)개의 섬이 m(1 ≤ m ≤ 1,000)개의 다리로 연결되어 있다. 각각의 다리는 서로 다른 두 섬을 연결하고 있으며, 서로 다른 두 섬은 최대 한 개의 다리로만 직접 연결되어 있다. 각각의 다리들의 튼튼한 정도는 서로 달라서, 각각의 다리마다 견딜 수 있는 무게의 제한이 다를 수 있다.
섬들 중, K(1 ≤ K ≤ 14)개의 서로 다른 섬에 각각 한 개씩 보석이 있다. 당신은 1번 섬에서 빈손으로 출발하여 최대한 많은 보석을 줍고 1번 섬으로 돌아오려 한다. 주의할 것은, 보석을 너무 많이 줍다 보면 다리를 건널 때 다리가 무게를 견디지 못하고 무너질 수 있다는 점이다. 따라서 당신은 다리가 무너지지 않는 한도 내에서 보석을 주워야 한다.
한 번 지난 적이 있는 다리와 섬을 여러 번 지날 수 있으며, 보석이 있는 섬을 지날 때에 그 보석을 줍지 않을 수도 있다고 하자.
입력 :
첫째 줄에 n, m, K가 주어진다. 다음 K개의 줄에는 보석이 있는 섬의 번호가 주어진다. 다음 m개의 줄에는 각 다리에 대한 정보를 나타내는 세 자연수 a, b, c(1 ≤ c ≤ 100)가 주어진다. 이는 a번 섬과 b번 섬이 다리로 연결되어 있는데, 그 다리가 최대 c개의 보석만을 견딜 수 있다는 의미이다. 예를 들어 c가 2라면, 그 다리를 지날 때 보석을 0, 1, 2개 가지고 있어야 한다는 의미이다. 3개 이상의 보석을 가지고 그 다리를 지나려고 하면 다리가 무너진다.
출력 :
첫째 줄에 주울 수 있는 보석의 최대 개수를 출력한다.
풀이 :
우선 일반 탐색보다 더 어렵게 느껴졌던 이유는 방문했거나 지나친 섬과 다리를 다시 재 방문할 수 있었고 보석을 줍지 않을 수도 있다는 점이었던 것 같다.
본 문제는 비트마스킹을 떠올리고 효과적으로 사용할 줄 안다면 쉽게 접근할 수 있는 문제인 듯 했다.
우선 방문했던 섬들을 재 방문할 수 있기 때문에 일반적인 DFS와 BFS 방식은 사용할 수 없을 것이라고 예상했다.
따라서 해당 섬에 도착했을 때 주운 보석들을 비트마스크로 표현해 visit 배열을 만든다면 중복 방문을 없앨 수 있을 것이라 생각했다.
또 이 방법으로 문제를 해결할 수 있을 것이라고 생각했던 큰 이유 중 하나는 섬의 갯수는 100개 이하이지만 그 중 보석을 가지고 있는 갯수는 14개 이하에 불과했기 때문에 비트마스크로 표현하기 충분하다고 느꼈다.
코드 :
코드 보기/접기
#include <iostream>
#include <vector>
#include <utility>
#include <queue>
#include <algorithm>
#define pii pair<int, int>
using namespace std;
typedef struct Node {
int idx, cnt, cntval;
};
bool visit[101][1 << 15];
vector<vector<pii>> adj;
vector<int> jewelry;
int n;
int bfs() {
int answer = 0;
queue<Node> q;
q.push(Node{1, 0, 0});
visit[1][0] = true;
while (!q.empty()) {
int curidx = q.front().idx, curcnt = q.front().cnt, curcntval = q.front().cntval;
q.pop();
if (curidx == 1) answer = max(answer, curcnt);
int size = adj[curidx].size();
for (int k = 0; k < size; k++) {
int cmpidx = adj[curidx][k].first, cmpweight = adj[curidx][k].second;
if (cmpweight < curcnt) continue;
if (!visit[cmpidx][curcntval]) {
visit[cmpidx][curcntval] = true;
q.push(Node{cmpidx, curcnt, curcntval});
}
if (jewelry[cmpidx] != -1) {
int cmpcntval = curcntval | (1 << jewelry[cmpidx]);
if (visit[cmpidx][cmpcntval]) continue;
visit[cmpidx][cmpcntval] = true;
q.push(Node{cmpidx, curcnt + 1, cmpcntval});
}
}
}
return answer;
}
int main() {
int m, k, input, i, fir, sec, val;
cin >> n >> m >> k;
jewelry.resize(n + 1, -1);
adj.resize(n + 1);
for (i = 1; i <= k; i++) {
cin >> input;
jewelry[input] = i;
}
while (m--) {
cin >> fir >> sec >> val;
adj[fir].emplace_back(sec, val);
adj[sec].emplace_back(fir, val);
}
cout << bfs() << '\n';
return 0;
}