백준 11562번 (백양로 브레이크, C++, Floyd-Warshall) [BAEKJOON]

백양로 브레이크

https://www.acmicpc.net/problem/11562

시간 제한메모리 제한제출정답맞힌 사람정답 비율
1 초256 MB26981354102447.828%

문제

서울 소재 Y모 대학교에서 대규모 공사를 진행하면서, 학교가 마치 미로처럼 변해버리고 말았다.

공사 이전까지는 어떤 건물에서 출발하더라도 다른 모든 건물로 갈 수 있는 길이 있었으나,

공사가 진행되면서 어떻게 한 건진 알 수 없지만 일방통행만 가능한 길이 많이 늘고 말았다.

컴퓨터과학과 학생 남규는 전공 수업을 듣고 교양 수업을 들으러 가던 중

길을 잃어 3일 밤낮을 헤매다가 공학관에서 종합관으로 가는 길은 존재하지 않는다는 결론을 내렸다.

3일 사이에 과제도 내지 못하고 출석도 하지 못해 학사경고 위기에 처한 남규는 전공을 살려

현재 일방통행인 길들 중 반드시 양방향으로 바꿔야만 하는 길이 몇 개인지 조사해 학교에 건의하기로 마음을 먹었다.

남규는 여러 건물들 사이를 직접 잇는 길들을 모두 조사했고,

그 중 어떤 길들이 일방통행인지, 또는 양방향 통행이 가능한지를 모두 체크했다.

남규의 프로그램은 간단하다.

출발지와 도착지를 입력하면 도착지까지 가기 위해 최소 몇 개의 길을 양방향으로 바꿔야만 하는지를 출력해준다.

프로그램이 완성되었다는 소문이 퍼지자, 남규처럼 길을 잃고 헤맨 경험이 있는 학생들은 남규에게 묻기 시작했다.

“공학관에서 대강당 갈 수 있어?”

“상경대 별관에서 학관으로는?”

남규는 매번 손으로 타이핑해 입력하고 결과를 보내주는 데에 지치고 말았다.

결국 앓아누운 남규를 위해 학생들의 질문을 해결할 새로운 프로그램을 만들어보자.

입력

첫 줄에 Y대학교 건물의 수 n과 길의 수 m이 주어진다. (n ≤ 250, m ≤ n * (n – 1) / 2 )

다음 m줄에 걸쳐, u v b (1 ≤ u ≤ n, 1 ≤ v ≤ n, u != v, b = 0 또는 1) 의 형태로 길에 대한 정보가 주어진다.

b가 0일 경우 u에서 v로 가는 일방통행 길인 것이고, b가 1일 경우 u와 v를 잇는 양방향 길이다.

어떤 두 건물 사이를 잇는 길은 최대 한 개이다.

다음 줄에 학생들의 질문의 수 k가 주어진다. (1 ≤ k ≤ 30,000)

다음 k줄에 걸쳐 s e (1 ≤ s ≤ n, 1 ≤ e ≤ n)의 형태로 학생들의 질문들이 주어진다.

이는 질문한 학생이 건물 s에서 건물 e로 가고 싶다는 의미이다.

출력

출력은 k줄에 걸쳐 이루어진다.

각 질문에 대해, 최소 몇 개의 일방통행인 길을 양방향 통행으로 바꿔야 출발지에서 도착지로 갈 수 있는지를 출력한다.

모든 길을 양방향으로 바꾸더라도 서로 도달 불가능한 건물은 없다.

예제 입력 1

4 3
1 2 0
2 3 1
3 4 0
7
1 1
1 2
2 1
1 4
4 1
2 3
4 3

예제 출력 1

0
0
1
0
2
0
1

출처

University > 연세대학교 > 2015 연세대학교 프로그래밍 경시대회 F번

알고리즘 분류


접근 방법

시간 초과 코드 (DFS)

DFS 탐색을 이용하여 도착지부터 출발지까지 최소 거리를 카운트하여 탐색한 코드

#include <iostream>
#include <vector>
#include <list>

using namespace std;

constexpr int INF = INT32_MAX / 2;

constexpr int MAX = 251;

vector<pair<int, int>> graph[MAX];

list<pair<int, int>> myList;

int disArr[MAX][MAX];
bool check[MAX];

// N : 도시의 수, M : 길의 수
int N, M, s, d, w, b, K, result;

void Tracking(int start, int now, int cnt)
{
	if (start == now || disArr[now][start] == 0) {
		result = min(result, cnt);
		return;
	}

	for (int j = 0; j < graph[now].size(); j++) {
		int v = graph[now][j].first; // 도착점
		int weight = graph[now][j].second; // 가중치

		if (check[v] == true) continue;

		if (weight == INF) {
			cnt++;
			check[v] = true;
			Tracking(start, v, cnt);
			check[v] = false;
			cnt--;	
		}
		else
		{
			check[v] = true;
			Tracking(start, v, cnt);
			check[v] = false;
		}
	}
}


int main()
{
	ios_base::sync_with_stdio(false); // scanf와 동기화를 비활성화
	// cin.tie(null); 코드는 cin과 cout의 묶음을 풀어줍니다.
	cin.tie(NULL);
	cout.tie(NULL);

	cin >> N >> M;

	// 길의 정보를 입력
	for (int i = 0; i < M; i++) {

		// 출발 s, 도착 d, b 양/단 방향
		cin >> s >> d >> b;

		if (b == 0) { // 단방향
			graph[s].push_back(make_pair(d, 0));
			graph[d].push_back(make_pair(s, INF));
		}
		else { // 양방향
			graph[s].push_back(make_pair(d, 0));
			graph[d].push_back(make_pair(s, 0));
		}

	}

	// 질문을 입력 받는다.
	cin >> K;
	pair<int, int> tempP;
	while (K--) {
		cin >> tempP.first >> tempP.second;
		myList.push_back(tempP);
	}




	// 최단 거리 배열 disArr 배열을 INF 초기화
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= N; j++) {

			if (i == j) disArr[i][j] = 0;
			else disArr[i][j] = INF;

		}
	}

	for (int i = 1; i <= N; i++) { // 시작 정점
		for (int j = 0; j < graph[i].size(); j++) {

			int v = graph[i][j].first; // 도착점
			int weight = graph[i][j].second; // 가중치

			if (disArr[i][v] > weight) 	disArr[i][v] = weight;

		}
	}

	// 무지성 플로이드
	for (int k = 1; k <= N; k++) {
		for (int i = 1; i <= N; i++) {
			for (int j = 1; j <= N; j++) {

				if (i == j) disArr[i][j] = 0;
				else disArr[i][j] = min(disArr[i][j], disArr[i][k] + disArr[k][j]);

			}
		}
	}

	for (auto it = myList.begin(); it != myList.end(); it++) {
		result = INF;
		if (disArr[it->first][it->second] == 0) cout << disArr[it->first][it->second] << "\n";
		else {
			Tracking(it->second, it->first, 0);
			cout << result << "\n";
		}
	}

	return 0;
}

통과된 코드

Floyd-Warshall 알고리즘이 어떤 식으로 작동하는지 안다면 뒤집는 부분에 왜 1 이 들어가는지 알 수 있다.

#include <iostream>
#include <vector>
#include <list>

using namespace std;

constexpr int INF = INT32_MAX / 2;

constexpr int MAX = 251;

vector<pair<int, int>> graph[MAX];

list<pair<int, int>> myList;

int disArr[MAX][MAX];

// N : 도시의 수, M : 길의 수
int N, M, s, d, w, b, K, result;

int main()
{
	ios_base::sync_with_stdio(false); // scanf와 동기화를 비활성화
	// cin.tie(null); 코드는 cin과 cout의 묶음을 풀어줍니다.
	cin.tie(NULL);
	cout.tie(NULL);

	cin >> N >> M;

	// 최단 거리 배열 disArr 배열을 INF 초기화
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= N; j++) {
			disArr[i][j] = INF;
		}
	}

	// 길의 정보를 입력
	for (int i = 0; i < M; i++) {

		// 출발 s, 도착 d, b 양/단 방향
		cin >> s >> d >> b;

		if (b == 0) { // 단방향
			graph[s].push_back(make_pair(d, 0));
			disArr[d][s] = 1; // (disArr[i][k] + disArr[k][j] vs INF)
		}
		else { // 양방향
			graph[s].push_back(make_pair(d, 0));
			graph[d].push_back(make_pair(s, 0));
		}

	}

	// 질문을 입력 받는다.
	cin >> K;

	pair<int, int> tempP;

	while (K--) {
		cin >> tempP.first >> tempP.second;
		myList.push_back(tempP);
	}

	for (int i = 1; i <= N; i++) { // 시작 정점
		for (int j = 0; j < graph[i].size(); j++) {

			int v = graph[i][j].first; // 도착점
			int weight = graph[i][j].second; // 가중치
			if (disArr[i][v] > weight) disArr[i][v] = weight;
		}
	}

	// 무지성 플로이드
	for (int k = 1; k <= N; k++) {
		for (int i = 1; i <= N; i++) {
			for (int j = 1; j <= N; j++) {

				if (i == j) disArr[i][j] = 0;
				else disArr[i][j] = min(disArr[i][j], disArr[i][k] + disArr[k][j]);

			}
		}
	}

	for (auto it = myList.begin(); it != myList.end(); it++) {
		cout << disArr[it->first][it->second] << "\n";		
	}

	return 0;
}

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤