https://www.acmicpc.net/problem/1697
1697번: 숨바꼭질
수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일
www.acmicpc.net
아직 dfs / bfs에 익숙하지 않아서 그런지 두 탐색법을 응용해 문제를 푸는게 아직은 쉽지가 않다.
이걸 왜 bfs로 풀어야 하는지조차 첨엔 의문이었다ㅎㅎ
bfs는 모든 가중치가 1일때 최단경로를 구하기 적합한 방법이라고 한다 (가중치값이 다 다르면 bfs가 무용지물일테니까)
한번 방문했던 곳을 왜 또 가면 안된다는건지도 이해가 안갔다. 더 좋은 경로가 더 뒤에서 나올수도 있지 않는가 생각했었다
(bfs에 대해 이해가 부족했음..)
그래프를 그려보며 차근차근 해보니 이해가 간다..앞서 이미 한 지점을 방문한적이 있다면 그 앞선 경로가 더 짧은 경로가 될수밖에 없다
bfs로 탐색해서 한 노드를 최초로 방문하게되는 시점이 최단경로가 되는것이다
반면 dfs는 한번에 가볼수 있는 모든 경로를 다 가보고 돌아오는 식이므로 목표지점까지 방문한다고 하더라도
그게 가장 짧은 경로라는 보장이 없다. 그래서 최단 경로를 구하는 이 문제에서는 bfs가 딱인것..
어느정도 이해는 되는데 코드로 구현하려니 또 막막했다
1. 몇번만에 가는지 세기 위해, count를 어찌 할지가 고민되었다
변수 하나를 두고 ++로 하나씩 증가시키려고 했던게 문제였다. 그러지말고 visit 배열을 하나 두고
현재위치인 now의 visit값에서 + 1한 값을 각각의 연결된 노드의 visit값으로 해주는것이 핵심..
2. 탐색할 값의 범위를 어떻게 할지도 고민이었다.
크기를 K까지만 해야할지, 더 크게 해야할지..첫번째 코드에서는 K(end)정도의 크기만큼만 해뒀었다(뒤에 적었지만 그래서 틀렸음)
3. 값이 5 1 이렇게 들어가는것처럼 시작점이 도착점보다 클 경우도 고려해야한다
예를들어 10 5의 값이 들어가는 경우를 생각해봤을때, 5 10이라면 한번만에 방문되겠지만
10에서 5로 가려면 한칸씩 뒤로 이동하는 경우밖에 없으므로 5가 답이 될것이다
4. 아차..주의할점..양방향 그래프가 아니다
암생각없이 양방향이라 생각하고 반대로도 간선을 추가해줬는데 이러면 잘못된 값이 들어가게 된다..
5->10으로는 갈수 있어도 그 반대는 안되는것처럼..
밑에있는건 틀렸습니다 뜬 코드이다
package baekjoonWeek4;
import java.io.*;
import java.util.*;
public class HideAndSeek { //백준 1697번 숨바꼭질 문제
static ArrayList<ArrayList<Integer>> al;
static Queue<Integer> queue = new LinkedList<>();
static int[] visited;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
StringTokenizer st = new StringTokenizer(br.readLine());
int start = Integer.parseInt(st.nextToken()); //시작할 위치
int end = Integer.parseInt(st.nextToken()); //도착할 위치
int max = end;
if (start > end) {
max = start;
}
visited = new int[max+1];
al = new ArrayList<>(max+ 1);
for (int i = 0; i < max + 1; i++) {
al.add(new ArrayList<>());
}
for (int i = 0; i <= max; i++) {
if (i - 1 >= 0 && i - 1 <= max) {
al.get(i).add(i - 1);
// al.get(i - 1).add(i);
}
if (i + 1 >= 0 && i + 1 <= max) {
al.get(i).add(i + 1);
// al.get(i + 1).add(i);
}
if (i * 2 >= 0 && i * 2 <= max) {
al.get(i).add(i * 2);
// al.get(i * 2).add(i);
}
}
bw.write(Integer.toString(bfs(start, end)));
bw.flush();
bw.close();
}
static int bfs(int s, int e) { //시작점, 도착점
int now = s; //현위치
queue.add(now);
while (now != e) { //e에 도달할떄까지
now = queue.remove();
for (int i : al.get(now)) {
if (visited[i] == 0 && i != s) { //범위가 0~end 내의 범위이면서 방문 안했을떄
queue.add(i);
visited[i] = visited[now] + 1; //이부분이 중요!
}
}
}
return visited[now];
}
}
한 30%정도에서 틀렸습니다가 뜨던데
이것저것 경계값도 넣어보고..다른 반례들도 넣어봤는데 뭐가 문제인지 못찾다가
혹시 큐/배열/리스트들의 크기를 0부터 N(또는 K,둘중 더 큰 수) + 1까지로만 해놓은게 문제인가 싶었다
예를들어 6, 11의 경우 6 - 12 - 11 이렇게 가는게 최선일텐데 11까지만 만들어두면 그렇게 안나오니까!!
그래서 크기를 K의 두배로 늘려주었다(0부터인거 생각해서 적어도 +1해주는거 잊지말기)
->K의 두배까지 늘릴 필요는 없다!! +2만 해줘도 된다. 왜냐면 일단 맨 앞의 0을 생각해서 +1, K+1값에서 한칸 뒤로 K로 이동하는 경우 생각해서 총 +2만 해주면 된다!!
그러니까 맞았습니다가 떴다 ~휴
관련 문제를 더 많이 풀어봐야겠다고 느꼈다
package baekjoonWeek4;
import java.io.*;
import java.util.*;
public class HideAndSeek { //백준 1697번 숨바꼭질 문제
static ArrayList<ArrayList<Integer>> al;
static Queue<Integer> queue = new LinkedList<>();
static int[] visited;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
StringTokenizer st = new StringTokenizer(br.readLine());
int start = Integer.parseInt(st.nextToken()); //시작할 위치
int end = Integer.parseInt(st.nextToken()); //도착할 위치
int max = end;
if (start > end) {
max = start;
}
max = max * 2 + 1;
visited = new int[max];
al = new ArrayList<>(max);
for (int i = 0; i < max; i++) {
al.add(new ArrayList<>());
}
for (int i = 0; i < max; i++) {
if (i - 1 >= 0 && i - 1 < max) {
al.get(i).add(i - 1);
}
if (i + 1 >= 0 && i + 1 < max) {
al.get(i).add(i + 1);
}
if (i * 2 >= 0 && i * 2 < max) {
al.get(i).add(i * 2);
}
}
bw.write(Integer.toString(bfs(start, end)));
bw.flush();
bw.close();
}
static int bfs(int s, int e) { //시작점, 도착점
int now = s; //현위치
queue.add(now);
while (now != e) { //e에 도달할떄까지
now = queue.remove();
for (int i : al.get(now)) {
if (visited[i] == 0) { //방문 안했을때,i가 시작점일경우 0이어야하니까 건너뜀
queue.add(i);
visited[i] = visited[now] + 1; //이부분이 중요!
}
}
}
return visited[now];
}
}
'알고리즘 > 그래프' 카테고리의 다른 글
[백준알고리즘] 그래프(bfs) - 13549번 숨바꼭질3 문제(다시해볼예정) (0) | 2023.04.24 |
---|---|
[백준알고리즘] 그래프(bfs) - 13913번 숨바꼭질4 문제 (0) | 2023.04.23 |
[백준알고리즘] 그래프 - 11724번 연결 요소의 개수 문제 (0) | 2023.04.17 |
[백준알고리즘] 그래프 - 1260번 DFS와 BFS (1) | 2023.04.17 |
[백준알고리즘] 그래프 - 13023 ABCDE 문제 (0) | 2023.04.16 |