다익스트라로 모든 노드를 다 방문하되, 흰 방에는 weight 0을, 검은 방에는 weight로 1을 준다. 그러면 흰 방을 우선으로 방문하면서 해당 노드까지 이동하면서 거쳐온 검은 방의 개수를 구할 수 있다!
재밌게 풀었고 풀면서 다익스트라 복습도 해서 좋았다.
다익스트라 알고리즘
- 그래프의 한 vertex(모든 edge가 0 또는 양수 weight를 가지는 )에서 모든 vertex까지의 최단 거리를 각각 구하는 알고리즘
- 최단 거리를 노드의 개수번 만큼 구하는 것보다 빠르다.
- edge의 방향성 유무는 상관 없음
- PQ나 heap을 사용하면
O((V+E)logV)
의 시간 복잡도를 가짐- 각 노드를 방문할 때마다 아직 방문하지 않은 노드로부터 현재까지 계산된 최단거리를 찾고
O(VlogV)
- 각 노드마다 이웃 노드의 최단거리를 갱신
O(ElogV)
하기 때문- 모든 edge를 확인해
O(E)
매번 PQ/heap에서 최단거리를 갱신O(logV)
해주기 때문. 이는 모든 노드를 방문해 확인하는 것O(logN)
보다 빠르다.
- 모든 edge를 확인해
- 각 노드를 방문할 때마다 아직 방문하지 않은 노드로부터 현재까지 계산된 최단거리를 찾고
알고리즘
- 변수 선언 및 초기화
- 노드 간 weight를 저장할 자료구조(adjacency graph/matrix 등)에 값 입력
- 시작점으로부터의 최단거리를 저장할 배열 dist[N]의 모든 원소를 INF로 초기화
- 각 노드의 방문 여부를 저장할 배열 visited[N]를 false로 초기화
- 노드들의 최단거리를 저장할 PQ/heap 선언
- 시작 노드를 PQ에 push
- PQ(값이 작은 순서대로 꺼냄)가 빌 때까지 3~9번 과정 반복
- PQ의 top을 pop해 현재 보고 있는 노드 A를 기준으로
- A의 모든 이웃 노드 B에 대하여 6~9번 수행
- 시작 노드로부터 A까지의 거리인
dist[A]
+ A에서 B까지의 거리(weight)weight[A][B]
, 즉 시작점에서부터 A를 거쳐 B로 가는 길이dist[A] + weight[A][B]
와, 시작점에서부터 바로 B까지 가는 길이dist[B]
값 비교 dist[A] + weight[A][B] < dist[B]
라면dist[B]
의 값을dist[A] + weight[A][B]
로 갱신- A를 방문했다고 표시하고 (
visited[A] = true
) - B를 PQ<거리, 노드이름>에 push
- 도착 노드를 방문하거나 혹은 도착노드를 방문하지 못했지만 PQ가 빈 경우(도착점까지 갈 수 없는 경우) 알고리즘 종료
구현
#include <iostream>
#include <cstring> // memset
#include <string>
#include <vector>
#include <utility> // pair
#include <queue> // pq
#define MAX 51
#define INF 0x7FFFFFFF
using namespace std;
int dist[MAX][MAX]; // current shortest distance from (1, 1)
bool isWhite[MAX][MAX]; // white: weight = 1, black: weight = 0
bool visited[MAX][MAX];
int main() {
ios_base::sync_with_stdio(false);
cin.tie(0);
int N, i, j, nowR, nowC;
string str;
priority_queue< pair< int, pair<int, int> >, vector< pair< int, pair<int, int> > >, greater< pair< int, pair<int, int> > > > pq; // (dist, (row, col))
memset(isWhite, true, sizeof(isWhite));
memset(visited, false, sizeof(visited));
cin >> N;
for (i = 1; i <= N; i++) {
cin >> str;
for (j = 1; j <= N; j++) {
dist[i][j] = INF;
if (str[j - 1] == '0') isWhite[i][j] = false;
}
}
nowR = 1, nowC = 1;
dist[nowR][nowC] = 0;
pq.push(make_pair(0, make_pair(nowR, nowC)));
while (!pq.empty()) {
int tmpDist = pq.top().first;
int nowR = pq.top().second.first;
int nowC = pq.top().second.second;
// cout << "now: (" << nowR << ", " << nowC << ") with distance " << tmpDist << endl;
pq.pop();
if (!visited[nowR][nowC]) {
visited[nowR][nowC] = true;
if (nowR - 1 >= 1) { // UP
if (tmpDist + (isWhite[nowR - 1][nowC] ? 0 : 1) < dist[nowR - 1][nowC]) {
dist[nowR - 1][nowC] = tmpDist + (isWhite[nowR - 1][nowC] ? 0 : 1);
}
if (!visited[nowR - 1][nowC]) pq.push(make_pair(dist[nowR - 1][nowC], make_pair(nowR - 1, nowC)));
// cout << "next: (" << nowR - 1 << ", " << nowC << ") with distance " << dist[nowR - 1][nowC] << endl;
}
if (nowR + 1 <= N) { // DOWN
if (tmpDist + (isWhite[nowR + 1][nowC] ? 0 : 1) < dist[nowR + 1][nowC]) {
dist[nowR + 1][nowC] = tmpDist + (isWhite[nowR + 1][nowC] ? 0 : 1);
}
if (!visited[nowR + 1][nowC]) pq.push(make_pair(dist[nowR + 1][nowC], make_pair(nowR + 1, nowC)));
// cout << "next: (" << nowR + 1 << ", " << nowC << ") with distance " << dist[nowR + 1][nowC] << endl;
}
if (nowC - 1 >= 1) { // LEFT
if (tmpDist + (isWhite[nowR][nowC - 1] ? 0 : 1) < dist[nowR][nowC - 1]) {
dist[nowR][nowC - 1] = tmpDist + (isWhite[nowR][nowC - 1] ? 0 : 1);
}
if (!visited[nowR][nowC - 1]) pq.push(make_pair(dist[nowR][nowC - 1], make_pair(nowR, nowC - 1)));
// cout << "next: (" << nowR << ", " << nowC - 1 << ") with distance " << dist[nowR][nowC - 1] << endl;
}
if (nowC + 1 <= N) { // RIGHT
if (tmpDist + (isWhite[nowR][nowC + 1] ? 0 : 1) < dist[nowR][nowC + 1]) {
dist[nowR][nowC + 1] = tmpDist + (isWhite[nowR][nowC + 1] ? 0 : 1);
}
if (!visited[nowR][nowC + 1]) pq.push(make_pair(dist[nowR][nowC + 1], make_pair(nowR, nowC + 1)));
// cout << "next: (" << nowR << ", " << nowC + 1 << ") with distance " << dist[nowR][nowC + 1] << endl;
}
}
}
cout << dist[N][N] << endl;
return 0;
}