※ 글쓴이는 취미로 코딩을 익혀보는 사람이라 정확하지 않은 내용을 담고 있을 수 있다 ※

 

이번에 볼 문제는 백준 1219번 문제인 오민식의 고민이다.
문제는 아래 링크를 확인하자.

www.acmicpc.net/problem/1219

 

1219번: 오민식의 고민

첫째 줄에 도착 도시에 도착할 때, 가지고 있는 돈의 액수의 최댓값을 출력한다. 만약 오민식이 도착 도시에 도착하는 것이 불가능할 때는 "gg"를 출력한다. 그리고, 오민식이 도착 도시에 도착

www.acmicpc.net

이 문제에서는 기본적으로는 어떤 그래프 위 출발 노드에서 도착 노드로 가는 경로 중, 가중치(버는 돈)가 가장 큰 경로를 탐색하는 문제이다. 경로에 따라 돈을 벌 수도 있고 잃을 수도 있으므로, 음수 가중치를 다룰 수 있는 알고리즘인 Bellman-Ford(벨만-포드) 알고리즘이 이 문제를 해결하는 데에 적합하다. 다만, 세부적으로 나누어 출력할 대상이 많으므로 신중하게 코드를 작성해야 한다.

 

다음은 이 문제를 틀리지 않기 위해 주의해야하는 점들이다.

 

1. 가중치가 양수인 사이클(돈을 무수히 벌 수 있는 사이클)이 존재한다고 항상 답이 "Gee"가 되지는 않는다. 이 사이클을 계속 빙빙 돌면 돈을 무수히 벌 수 있는 것은 사실이지만, 돈을 벌더라도 경로의 종점이 되어야 하는 "도착 도시"로 갈 수 없다면 이러한 사이클의 존재는 의미가 없어진다.

2. 문제에서 주어진 100개의 도시가 하나의 큰 사이클을 이루고, 각 도시를 방문할 때마다 1,000,000만큼의 돈을 번다면, 각 에지를 아직 한번씩만 살펴보았는데도 100,000,000만큼의 가중치가 쌓이게 된다. 이 사이클을 100번 돌게 되면 10,000,000,000의 가중치가 쌓이게 되는데, 이는 32bit 정수의 표현범위를 넘어가는 수이다. 따라서, 이 문제를 해결하기 해서는 가중치를 저장하는 배열을 64bit 정수로 선언해야한다.

 

글쓴이는 이 문제의 출력조건을 아래와 같이 구분하였다.

1) 도착 도시에 도달할 수 없는 경우 "gg"를 출력

2) 가중치가 양수인 사이클(돈을 무수히 벌 수 있는 사이클) 위의 점들에서 도착 도시로 갈 수 있는 경로가 있는지 DFS로 확인 후, 존재한다면 "Gee" 출력

3) 그 외의 경우 Bellman-Ford로 계산된 경로의 최대가중치 출력

 

아래는 제출한 소스코드이다.

#include <iostream>
#include <vector>
using namespace std;

vector<int> G[100];
vector<int> Gi[100];
long long money[100];
int way[100][3];

vector<int> stk;
bool visited[100];

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);

	int N, S, E, M; cin >> N >> S >> E >> M;
	for (int i = 0; i < M; i++) {
		cin >> way[i][0] >> way[i][1] >> way[i][2];
		G[way[i][0]].push_back(way[i][1]);
		Gi[way[i][1]].push_back(i);
	}

	for (int i = 0; i < N; i++) {
		money[i] = -1000000007;
	}

	for (int i = 0; i < N; i++) {
		int x; cin >> x;
		for (auto node : Gi[i]) {
			way[node][2] = x - way[node][2];
		}
		if (i == S) money[i] = x;
	}

	for (int i = 0; i < N; i++) {
		for (int j = 0; j < M; j++) {
			if (money[way[j][0]] == -1000000007) continue;
			if (money[way[j][1]] < money[way[j][0]] + way[j][2])
				money[way[j][1]] = money[way[j][0]] + way[j][2];
		}
	}

	// cycle 위에 있는 점들을 stk에 넣고 DFS를 통해 이 점들에서 도착 도시로 갈 수 있는지 확인
	for (int j = 0; j < M; j++) {
		if (money[way[j][0]] == -1000000007) continue;
		if (money[way[j][1]] < money[way[j][0]] + way[j][2]) {
			if (!visited[way[j][0]]) {
				stk.push_back(way[j][0]);
				visited[way[j][0]] = 1;
			}
		}
	}

	// dfs
	while (!stk.empty()) {
		int current = stk.back();
		stk.pop_back();
		for (auto node : G[current]) {
			if (visited[node]) continue;
			visited[node] = 1;
			stk.push_back(node);
		}
	}

	if (money[E] == -1000000007) cout << "gg";
	else if (visited[E]) cout << "Gee";
	else cout << money[E];
}
728x90

'BOJ' 카테고리의 다른 글

[BOJ 1101 // C++] 스티커 정리 1  (0) 2021.05.19
[BOJ 10977 // C++] 음식 조합 세기  (0) 2021.05.18
[BOJ 11657 // C++] 타임머신  (0) 2021.05.16
[BOJ 1865 // C++] 웜홀  (0) 2021.05.15
[BOJ 11563 // C++] 연돌이와 고잠녀  (0) 2021.05.14

+ Recent posts