알고리즘/백준

9205_맥주 마시면서 걸어가기

베리영young 2024. 10. 25. 23:05

사용 알고리즘: bfs

사용 언어: java

 

 

일단 한 번 틀림

이때 로직은, pq를 만들어서, 시작점에서 가장 가까운 편의점부터 줄 세움.

package week21;

import java.util.*;
import java.io.*;

public class Main_bj_9205_맥주마시면서걸어가기 {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int testcase = sc.nextInt();
		for(int t=0; t<testcase; t++) {
			int n = sc.nextInt(); //편의점의 개수
			int[][] endPoint = new int[2][2]; //집,락페
			Queue<int[]> pq = new PriorityQueue<>((o1, o2) 
					-> (Math.abs(endPoint[0][0]-o1[0])+Math.abs(endPoint[1][0]-o1[1])) - (Math.abs(endPoint[0][1]-o2[0])+Math.abs(endPoint[1][1]-o2[1]))); //집에서 가까운 순으로 정렬
			endPoint[0][0] = sc.nextInt();
			endPoint[0][1] = sc.nextInt(); //집 좌표
			
			for(int i=0; i<n; i++) {
				int x = sc.nextInt();
				int y = sc.nextInt();
				
				pq.add(new int[] {x, y});
			}
			
			endPoint[1][0] = sc.nextInt();
			endPoint[1][1] = sc.nextInt(); //락페 좌표
			
			if(canGo(n, endPoint, pq)) System.out.println("happy");
			else System.out.println("sad");
			
//			while(!pq.isEmpty()) {
//				System.out.println(Arrays.toString(pq.poll()));
//			}
			
		}//
	}
	
	private static boolean canGo(int n, int[][] endPoint, Queue<int[]> pq) {
		int cur[] = new int[] {endPoint[0][0], endPoint[0][1]};
		
		while(!pq.isEmpty()) {
			int[] nxtConv = pq.poll();
			int goStraight = canTmpGo(cur, endPoint[1]);
			int goNxtConv = canTmpGo(cur, nxtConv);
			
			if(goStraight >= 0) //편의점을 거치치 않고도 바로 갈 수 있음
				return true; 
			
			if(goNxtConv < 0) //다음 편의점조차 갈 수 없으면 못 감
				return false;
			
			//다음 편의점으로만 갈 수 있는 경우, 맥주를 20개 채워 온다.
			cur = nxtConv;
		}
		//마지막 장소에서 확인(모든 편의점을 거쳐야 할 때는, 이미 pq가 비어있는 상태이기 때문에 한 번 더 확인 필요)
		int goStraight = canTmpGo(cur, endPoint[1]);
		
		if(goStraight >= 0) //편의점을 거치치 않고도 바로 갈 수 있음
			return true; 
		
		return false;
	}
	
	private static int canTmpGo(int[] start, int[] end) {
		return Math.abs(start[0] - end[0]) + Math.abs(start[1] - end[1]) <= 1000 ? 1 : -1;
		
		//맥주가 최대로 찼을 떄, 최대 50*20 = 1000m 움직일 수 있음.
		//따라서 '거리'가 1000 이하이면 갈 수 있음
		
	}

}

 

 

위의 코드의 문제점? -> 시작점에서 가까운 편의점으로 옮겼을 때, 그 다음 가까운 애가 꼭 현재 편의점과 제일 가까운 애라고 할 수 없음.

고친 코드는, 시작점에서 20병의 맥주 이내로 갈 수 있는 모든 좌표를 queue에 넣고, 그 편의점을 기준으로 해서 또 다시 다음 움직일 곳을 찾아간다.

package week21;

import java.util.*;
import java.io.*;

public class Main_bj_9205_맥주마시면서걸어가기 {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int testcase = sc.nextInt();
		for(int t=0; t<testcase; t++) {
			int n = sc.nextInt(); //편의점의 개수
			int[][] endPoint = new int[2][2]; //집,락페
			List<int[]> convens = new ArrayList<>();
			endPoint[0][0] = sc.nextInt();
			endPoint[0][1] = sc.nextInt(); //집 좌표
			
			for(int i=0; i<n; i++) {
				int x = sc.nextInt();
				int y = sc.nextInt();
				
				convens.add(new int[] {x, y});
			}
			
			endPoint[1][0] = sc.nextInt();
			endPoint[1][1] = sc.nextInt(); //락페 좌표
			
			if(canGo(n, endPoint, convens) >= 0) System.out.println("happy");
			else System.out.println("sad");
			
//			while(!pq.isEmpty()) {
//				System.out.println(Arrays.toString(pq.poll()));
//			}
			
		}//
	}
	
	private static int canGo(int n, int[][] endPoint, List<int[]> convens) {
		Queue<Beer> q = new ArrayDeque<>();
		q.add(new Beer(endPoint[0], 0)); //좌표, 움직인 횟수
		while(!q.isEmpty()) {
			Beer cur = q.poll();
			
			if(canTmpGo(cur.point, endPoint[1]) > 0) {
				//다음 편의점 거치지 않고 갈 수 있으면
				return cur.move;
			}
			
			//남은 편의점 중에서, 50*20 이내 거리에 있는 편의점 집어 넣기
			for(int i=convens.size() - 1; i>=0; i--) {
				if(canTmpGo(cur.point, convens.get(i)) > 0) {
					q.add(new Beer(convens.get(i), cur.move + 1));
					convens.remove(i);
				}
			}
		}
		return -1; //못 가면 음수 출력
	}
	
	private static int canTmpGo(int[] start, int[] end) {
		return Math.abs(start[0] - end[0]) + Math.abs(start[1] - end[1]) <= 1000 ? 1 : -1;
		
		//맥주가 최대로 찼을 떄, 최대 50*20 = 1000m 움직일 수 있음.
		//따라서 '거리'가 1000 이하이면 갈 수 있음
		
	}

}
class Beer {
	int[] point;
	int move;
	public Beer(int[] point, int move) {
		this.point = point;
		this.move = move;
	}
}