https://www.acmicpc.net/problem/16118
16118번: 달빛 여우
첫 줄에 나무 그루터기의 개수와 오솔길의 개수를 의미하는 정수 N, M(2 ≤ N ≤ 4,000, 1 ≤ M ≤ 100,000)이 주어진다. 두 번째 줄부터 M개의 줄에 걸쳐 각 줄에 세 개의 정수 a, b, d(1 ≤ a, b ≤ N, a ≠ b
www.acmicpc.net
다익스트라를 살짝 변형한 문제이다. 여우에 대해 다익스트라 탐색을 실행하는 경우와 늑대에 대해 다익스트라 탐색을 실행하는 경우를 따로 생각해야 하는데, 늑대에 대해 다익스트라 탐색을 실행하는 경우 오솔길을 홀수 번 이용하는 경우와 짝수 번 이용하는 경우를 나누어서 계산해야 한다.
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
#define INF 0x3f3f3f3f
#define MAX_N 4000
// 그래프의 각 에지
class edge {
public:
// cost: 에지의 비용, node: 도착 노드
int cost, node;
edge(int cost_, int node_) { this->cost = cost_; this->node = node_; }
};
// 시작 노드에서 도착 노드로의 경로
class path : public edge {
public:
// 현재 경로를 이루는 오솔길의 수가 홀수라면 true, 아니라면 false
bool is_odd;
path(int c, int n, bool is_odd_) : edge(c, n) { this->is_odd = is_odd_; }
};
// 우선순위 큐에 넣기 위한 연산자 오버로딩
bool operator>(path e1, path e2) { return e1.cost > e2.cost; }
// 여우가 이동하는 경로를 찾을 차례라면 true, 아니라면 false
bool fox = true;
// N: 그루터기의 수, M: 오솔길의 수
// cost[i][j]: 1번 그루터기에서 j번 그루터기까지의 경로의 최소 비용
// i가 0 혹은 1일 경우 최소 비용 경로를 이루는 오솔길의 수가 홀수 / 짝수인 경우,
// i가 2일 경우 여우의 경로
int N, M, cost[3][MAX_N];
// 다익스트라 탐색을 위한 우선순위 큐
priority_queue<path, vector<path>, greater<path>> q;
// road[i]: i번 그루터기와 연결된 오솔길
vector<edge> road[MAX_N];
// 1번 그루터기에서 시작한 경로 p에서 오솔길 하나를 통해 이동 가능한 경로 탐색
void push(path p) {
// mul: 소숫점 계산을 피하기 위해 임의로 정한
// 여우의 이동 비용에는 항상 2를,
// 늑대의 홀수번째 비용에는 1을, 짝수번째 비용에는 4를 곱해야 한다
// i, ii: cost의 인덱싱을 위한 변수
// cost_: 경로 p에서 오솔길을 한 번 거친 후의 비용
int mul = fox ? 2 : (p.is_odd ? 4 : 1),
i = fox ? 2 : p.is_odd,
ii = fox ? 2 : !p.is_odd,
cost_;
// 만일 p의 비용이 기존에 알려진 비용보다 작다면 비용을 갱신
if (cost[i][p.node] > p.cost) cost[i][p.node] = p.cost;
// 그렇지 않다면 메모리 초과 방지를 위해 return
else return;
// p의 도착 노드에 연결된 모든 오솔길 r에 대해
for (auto r : road[p.node]) {
// r을 거친 비용을 한 번 계산한 뒤
cost_ = p.cost + r.cost * mul;
// 계산한 비용이 기존 비용보다 작다면 이 경로를 q에 push
if (cost[ii][r.node] > cost_) q.push({cost_, r.node, !p.is_odd});
}
}
// 다익스트라 알고리즘을 통해 여우와 늑대의 최적 이동 경로를 각각 계산
void dijkstra() {
// q에 시작 노드를 push한 뒤
q.push({0, 0, 0});
// 가능한 모든 경로를 비용 순으로 탐색
while (!q.empty()) {
push(q.top());
q.pop();
}
// 여우의 경로 탐색이 끝났다면 늑대의 경로 탐색을 실행
fox = !fox;
}
int main() {
// 입출력 속도 향상
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
// a, b, d: 두 그루터기 a와 b를 잇는 오솔길의 비용 d
// ans: 여우가 늑대보다 빨리 도착할 수 있는 그루터기의 수
int a, b, d, ans = 0;
// 문제의 조건을 입력받은 뒤
cin >> N >> M;
while (M--) {
cin >> a >> b >> d;
road[--a].push_back({ d, --b });
road[b].push_back({d, a});
}
// cost를 모두 INF로 초기화
fill_n(cost[0], 3 * MAX_N, INF);
// 다익스트라를 두 번 실행해 여우의 이동 경로와 늑대의 이동 경로를 각각 계산
dijkstra();
dijkstra();
// 1번 그루터기를 제외한 모든 그루터기에 대해
// 여우가 늑대보다 반드시 더 빨리 도착할 수 있는 그루터기의 수를 계산
for (int i = 1; i < N; i++)
if (cost[0][i] < cost[1][i] && cost[0][i] < cost[2][i]) ans++;
// 여우가 늑대보다 빨리 도착할 수 있는 그루터기의 수를 출력
cout << ans << '\n';
return 0;
}
'알고리즘 > 문제 풀이' 카테고리의 다른 글
백준 5847번: Milk Scheduling (0) | 2022.06.30 |
---|---|
백준 23358번: Quack Strikes Back (Easy) (0) | 2022.06.30 |
백준 14658번: 하늘에서 별똥별이 빗발친다 (0) | 2022.06.16 |
백준 14499번: 주사위 굴리기 (0) | 2022.05.31 |
백준 16564번: 히오스 프로게이머 (0) | 2022.05.27 |