포스트

BOJ 35002 검은칠된 격자에 보내는 최단경로

sorohue가 PS하는 블로그

BOJ 35002 검은칠된 격자에 보내는 최단경로

문제 링크입니다.

문제 요약

격자에서 특정 두 칸 사이의 최단 경로가 $D$가 되도록 각 칸에 $0$ 이상 $9$ 이하의 가중치를 부여하세요.

풀이

가중치를 부여할 수 있는 모든 칸에 $0$을 부여했을 때 최단 경로가 가장 짧고, $9$를 부여했을 때 최단 경로가 가장 깁니다. 나아가 어떤 격자에서 임의의 칸의 가중치를 $1$ 올릴 때마다 최단 경로의 길이는 $1$ 증가하거나 유지됩니다.

이로부터, 부여한 가중치의 합으로 이분 탐색을 수행하면서 가중치를 임의로 배분해도 답이 존재한다면 항상 찾을 수 있음이 보장됩니다. 가중치 합에 맞게 적당히 가중치를 배분하고 다익스트라나 다이얼을 돌려 최단 경로를 찾아 나가면 문제를 해결할 수 있습니다.

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include<bits/stdc++.h>
#define mid (l+r>>1)
using namespace std;
using ll = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;

const int dy[] = {-1,1,0,0}, dx[] = {0,0,-1,1};
char a[777][777], b[777][777]; int d[777][777], tot, sy, sx, ey, ex, k, n, m;

void build(int x){
	int offset = x/tot; x %= tot;
	for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++){b[i][j] = a[i][j]; if(b[i][j] == '.') b[i][j] = '0'+offset+(x-->0);}
}

int bfs(){
	queue<pii> q[10]; auto qsize = [&](){int ret = 0; for(int i = 0; i < 10; i++) ret += q[i].size(); return ret;};
	int idx = b[sy][sx]-'0'; q[idx].emplace(sy, sx); memset(d, 15, sizeof(d)); d[sy][sx] = idx;
	while(qsize()){
		while(q[idx].size()){
			auto [y, x] = q[idx].front(); q[idx].pop();
			if(d[y][x]%10 != idx) continue;
			for(int dir = 0; dir < 4; dir++){
				int ny = y+dy[dir], nx = x+dx[dir];
				if(b[ny][nx] == '#') continue;
				if(d[ny][nx] > d[y][x]+b[ny][nx]-'0'){
					d[ny][nx] = d[y][x]+b[ny][nx]-'0';
					q[d[ny][nx]%10].emplace(ny, nx);
				}
			}
		}
		idx++; idx %= 10;
	}
	return d[ey][ex];
}

int main(){
	cin.tie(0);cout.tie(0);ios::sync_with_stdio(false); memset(a, '#', sizeof(a)); memset(b, '#', sizeof(b));
	cin >> n >> m; for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++){cin >> a[i][j]; if(a[i][j] == '.') tot++;}
	cin >> sy >> sx >> ey >> ex >> k;
	int l = 0, r = tot*9;
	while(l < r){build(mid); if(bfs() < k) l = mid+1; else r = mid;}
	build(mid); if(bfs() != k) return !(cout << -1);
	for(int i = 1; i <= n; i++, cout << '\n') for(int j = 1; j <= m; j++) cout << b[i][j];
}
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.