baekjoon

[백준] 16118 달빛 여우

윤만석 2024. 4. 11. 18:38

문제

관악산 기슭에는 보름달을 기다리는 달빛 여우가 한 마리 살고 있다. 달빛 여우가 보름달의 달빛을 받으면 아름다운 구미호로 변신할 수 있다. 하지만 보름달을 기다리는 건 달빛 여우뿐만이 아니다. 달빛을 받아서 멋진 늑대인간이 되고 싶어 하는 달빛 늑대도 한 마리 살고 있다.

관악산에는 1번부터 N번까지의 번호가 붙은 N개의 나무 그루터기가 있고, 그루터기들 사이에는 M개의 오솔길이 나 있다. 오솔길은 어떤 방향으로든 지나갈 수 있으며, 어떤 두 그루터기 사이에 두 개 이상의 오솔길이 나 있는 경우는 없다. 달빛 여우와 달빛 늑대는 1번 나무 그루터기에서 살고 있다.

보름달이 뜨면 나무 그루터기들 중 하나가 달빛을 받아 밝게 빛나게 된다. 그러면 달빛 여우와 달빛 늑대는 먼저 달빛을 독차지하기 위해 최대한 빨리 오솔길을 따라서 그 그루터기로 달려가야 한다. 이때 달빛 여우는 늘 일정한 속도로 달려가는 반면, 달빛 늑대는 달빛 여우보다 더 빠르게 달릴 수 있지만 체력이 부족하기 때문에 다른 전략을 사용한다. 달빛 늑대는 출발할 때 오솔길 하나를 달빛 여우의 두 배의 속도로 달려가고, 그 뒤로는 오솔길 하나를 달빛 여우의 절반의 속도로 걸어가며 체력을 회복하고 나서 다음 오솔길을 다시 달빛 여우의 두 배의 속도로 달려가는 것을 반복한다. 달빛 여우와 달빛 늑대는 각자 가장 빠르게 달빛이 비치는 그루터기까지 다다를 수 있는 경로로 이동한다. 따라서 둘의 이동 경로가 서로 다를 수도 있다.

출제자는 관악산의 모든 동물을 사랑하지만, 이번에는 달빛 여우를 조금 더 사랑해 주기로 했다. 그래서 달빛 여우가 달빛 늑대보다 먼저 도착할 수 있는 그루터기에 달빛을 비춰 주려고 한다. 이런 그루터기가 몇 개나 있는지 알아보자.

입력

첫 줄에 나무 그루터기의 개수와 오솔길의 개수를 의미하는 정수 N, M(2 ≤ N ≤ 4,000, 1 ≤ M ≤ 100,000)이 주어진다.

두 번째 줄부터 M개의 줄에 걸쳐 각 줄에 세 개의 정수 a, b, d(1 ≤ a, b  N, a ≠ b, 1 ≤ d ≤ 100,000)가 주어진다. 이는 a번 그루터기와 b번 그루터기 사이에 길이가 d인 오솔길이 나 있음을 의미한다.

출력

첫 줄에 달빛 여우가 달빛 늑대보다 먼저 도착할 수 있는 나무 그루터기의 개수를 출력한다.

 

달빛여우가 1번부터 도착지점까지의 최단거리는 위의 그래프 그대로 다익스트라를 진행합니다.

 

하지만 달빛늑대는 weight가 2배, 1/2배 2배 1/2배.... 반복되기때문에

한 노드를 반으로 쪼개서 다익스트라를 진행하면 됩니다.

 

#include<bits/stdc++.h>
#define FAST ios_base::sync_with_stdio(false),cin.tie(NULL);
#define mset(v) memset(v,0,sizeof(v));
#define rep(i,a) for(int i=0;i<a;++i)
#define REP(i,a) for(int i=1;i<=a;++i)

using namespace std;

typedef long long ll;
typedef pair<int, int> pi;
typedef tuple<int, int, int>ti;
typedef vector<int> vi;
typedef vector<vector<int>> vvi;
int dy[4] = { -1,0,1,0 }, dx[4] = { 0,1,0,-1 };

int N, M, d1[4001], v1[4001], v2[4001][2];
double d2[4001][2];
vector<pi> adj[4001];
void dij1(int s) {
	fill(d1, d1 + N + 1, 0x3f3f3f3f);
	d1[s] = 0;
	priority_queue<pi, vector<pi>, greater<pi>>pq;
	pq.push({ d1[s],s });
	while (!pq.empty()) {
		auto [dist, cur] = pq.top();
		pq.pop();
		if (v1[cur])continue;
		v1[cur] = 1;
		for (auto [next, cost] : adj[cur]) {
			if (d1[next] > d1[cur] + cost) {
				d1[next] = d1[cur] + cost;
				pq.push({ d1[next],next });
			}
		}
	}
}
struct node {
	double dist;
	int cur;
	bool s;
	
	bool operator<(const node& d) const {
		return dist > d.dist;
	}
};
void dij2(int s) {
	REP(i, N) {
		rep(j, 2)d2[i][j] = 987654321;
	}
	d2[s][0] = 0;
	//d2[s][1] = 987654321;
	priority_queue<node>pq;
	pq.push({ d2[s][0], s, 0}); //0 : 2배로 나가야함 , 1: 1/2배로 나가야함
	while (!pq.empty()) {
		auto [dist, cur, s] = pq.top();
		pq.pop();
		if (v2[cur][s])continue;
		v2[cur][s] = 1;
		for (auto [next, cost] : adj[cur]) {
			if (s == 0) {
				if (d2[next][1] > d2[cur][0] + (double)cost / 2) {
					d2[next][1] = d2[cur][0] + (double)cost / 2;
					pq.push({d2[next][1], next, 1});
				}
			}
			else {
				if (d2[next][0] > d2[cur][1] + cost * 2) {
					d2[next][0] = d2[cur][1] + cost * 2;
					pq.push({ d2[next][0], next, 0 });
				}
			}
		}
	}
}
int main() {
	FAST;
	cin >> N >> M;
	while (M--) {
		int a, b, c;
		cin >> a >> b >> c;
		adj[a].push_back({ b,c });
		adj[b].push_back({ a,c });
	}
	dij1(1);
	dij2(1);
	int t = 0;
	for (int i = 2; i <= N; i++) {
		if (d1[i] < d2[i][0] && d1[i] < d2[i][1]) {
			t++;
		}
	}
	cout << t;
}

'baekjoon' 카테고리의 다른 글

[14940] 쉬운 최단거리  (2) 2024.09.16
[백준] prim's algorithm, kruskal algorithm  (0) 2024.05.07
[백준] 1719 택배  (0) 2024.04.10
[백준] 2011 암호코드  (0) 2024.03.26
[백준] 5557. 1학년  (0) 2024.03.25