알고리즘/코드트리

코드트리빵[G2]

블랑v 2023. 11. 2. 19:33

 

https://www.codetree.ai/training-field/frequent-problems/problems/codetree-mon-bread/description?page=1&pageSize=20

 

코드트리 | 코딩테스트 준비를 위한 알고리즘 정석

국가대표가 만든 코딩 공부의 가이드북 코딩 왕초보부터 꿈의 직장 코테 합격까지, 국가대표가 엄선한 커리큘럼으로 준비해보세요.

www.codetree.ai

 

package codeTree;

//https://www.codetree.ai/training-field/frequent-problems/problems/codetree-mon-bread/description?page=1&pageSize=20
//코드트리빵
//1357 ~ 1631
//List.remove 쓰지 말것

import java.util.*;
import java.io.*;
public class G2_CoBread {
	static int N, M;
	static int[][] map;
	
	static final int[] DR =  {-1, 0, 0, 1}; //U L R D
	static final int[] DC =  {0, -1, 1, 0};
	static List<Pos> baseCamps = new ArrayList<>();
	static List<Person> persons = new ArrayList<>();
		
	//베이스캠프 = 1, 편의점 = 2, 이제 갈수 없는곳 = -1
	public static void main(String[] args) throws Exception {
		setting();
		//로직
		//최단거리 찾기 로직이 제일 중요함!!
		int time = 0;
		while(true) {
//			System.out.println("[시간] : " + time);
			move(); //1. 최단거리 이동 : 그냥 거리 계산이 아니라 BFS 해야할듯 (못가는 곳이 있으니)
			check(); //2. 편의점 체크(이제 맵에서 -1 처리)
			if(AlluserIsOut()) break; //맵 탐색 로직 후 모두가 찾았다면
			if(time < M) input(time); //3. 베이스캠프 입장
			time++;
		}
		System.out.println(time + 1);
	}
	
	static boolean AlluserIsOut() {
		for(int i = 0; i < persons.size(); i++) {
			Person now = persons.get(i);
			if(now.r != -2 || now.c != -2) return false;
		}
		return true;
	}
	
	static void check() {
		for(int i = 0; i < persons.size(); i++) {
			check_each(persons.get(i), i); //만약 도착했다면 정보를 갱신한다 
		}
		
	}
	
	static void check_each(Person now, int index) {
		if(now.r == now.pr && now.c == now.pc) {
			map[now.r][now.c] = -1; //편의점 지우기
			now.r = -2; now.c = -2; //-2 : 완전 도착 후 제거
		}
	}
	
	static void move() {
		int size = persons.size();
		for(int i = 0; i < size; i++) {
			Person person = persons.get(i);
			if((person.r == -1 && person.c == -1) || (person.r == -2 && person.c == -2)) continue; // 아직 입장 안함, 나감
//			System.out.println(i + " 번째 유저 이동 ");
			move_eachOther(person);
		}
	}
	
	static void move_eachOther(Person now) {
		//편의점 방향을 향해 움직여야 함
		
		//최단거리 찾기
		int minDist = Integer.MAX_VALUE;
		int bestIdx = -1; //최단거리 방법
		Pos shop = new Pos(now.pr, now.pc);
		
		//4방향 우선순위 순서대로 탐색
		for(int i = 0; i < 4; i++) {
			int nr = now.r + DR[i];
			int nc = now.c + DC[i];
			int nowDist = bfs(new Pos(nr, nc), shop); //최단거리 탐색해보기
			if(nowDist < minDist) {
				minDist = nowDist;
				bestIdx = i;
			}
		}
		
//		System.out.println(now.r + " " + now.c + " 에서 방향 : ");
		//실제 이동
		now.r = now.r + DR[bestIdx];
		now.c = now.c + DC[bestIdx];
//		System.out.println(now.r + " " + now.c);
	}
	
	static void input(int time) throws Exception {
		Person now = persons.get(time);
		Pos shop = new Pos(now.pr, now.pc);
		
		//모든 베이스캠프 거리 탐색
		int minDist = Integer.MAX_VALUE;
		int br = 9999; int bc = 9999; int idx = 9999; //인덱스와 좌표
		for(int i = 0; i < baseCamps.size(); i++) {
			Pos nowCamp = baseCamps.get(i);
			if(nowCamp.r == -2 && nowCamp.c == -2) continue; //판매완료 캠프
			int nowDist = bfs(nowCamp, shop); //해당 베이스캠프와의 거리 판단 후 최단거리일 경우 갱신
			if(minDist > nowDist || 
					(minDist == nowDist && br > nowCamp.r) || 
					(minDist == nowDist && br == nowCamp.r && bc > nowCamp.c)
				) { //해당 정보로 갱신
				idx = i;
				br = nowCamp.r;
				bc = nowCamp.c;
				minDist = nowDist;
			}
		}
		
		//최적 베이스캠프 찾음
		map[br][bc] = -1; 		//해당 베이스캠프 -1 처리
		baseCamps.get(idx).r = -2;  //베이스캠프 리스트에서 제거
		baseCamps.get(idx).c = -2;  //베이스캠프 리스트에서 제거
		
		//사람 이동
		now.r = br;
		now.c = bc;
		
//		System.out.println("[input]사람 이동 : " + br + " " + bc);
	}
	
	static int bfs(Pos start, Pos end) {
		//시작 끝 좌표의 최단거리를 계산
		int minDist = Integer.MAX_VALUE;
		Queue<int[]> bfsQueue = new ArrayDeque<>();
		boolean[][] visit = new boolean[N][N];
		
		bfsQueue.add(new int[] {start.r, start.c, 0});
		while(!bfsQueue.isEmpty()) {
			int[] now = bfsQueue.poll();
			int r = now[0];
			int c = now[1];
			int dist = now[2];
			if(cantGo(r, c) || visit[r][c] || map[r][c] == -1) continue; //후순위 로직
			visit[r][c] = true; //방문 처리
			if(r == end.r && c == end.c) { //목표 좌표일 경우
				minDist = Math.min(minDist, dist); //최단거리 비교
				continue;
			}
			
			for(int i = 0; i < 4; i++) {
				int nr = r + DR[i];
				int nc = c + DC[i];
				if(cantGo(nr, nc) || visit[nr][nc] || map[r][c] == -1) continue; //방문한 장소, 맵 바깥, 못 가는 위치 스킵
				bfsQueue.add(new int[] {nr, nc, dist + 1});
			}
		}
		return minDist;
	}
	
	static void log() {
		for(int i = 0; i < N; i++) {
			for(int j = 0; j < N; j++) {
				System.out.print(map[i][j]+"\t");
			}
			System.out.println();
		}
		System.out.println("--------------");
	}
	
	static void setting() throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		N = Integer.parseInt(st.nextToken());
		M = Integer.parseInt(st.nextToken());
		map = new int[N][N];
		
		//맵 입력
		for(int i = 0; i < N; i++) {
			st = new StringTokenizer(br.readLine());
			for(int j = 0; j < N; j++) {
				map[i][j] = Integer.parseInt(st.nextToken());
				if(map[i][j] == 1) baseCamps.add(new Pos(i, j));
			}
		}
		
		//사람 입력
		for(int i = 0; i < M; i++) {
			Person person = new Person();
			st = new StringTokenizer(br.readLine());
			person.r = -1;
			person.c = -1;
			person.pr = Integer.parseInt(st.nextToken()) - 1;
			person.pc = Integer.parseInt(st.nextToken()) - 1;
			persons.add(person);
		}
	}
	
	static boolean cantGo(int r, int c) {
		if(r < 0 || c < 0 || r >= N || c >= N) return true;
		return false;
	}
}

class Pos {
	int r, c;
	Pos(int r, int c) {
		this.r = r;
		this.c = c;
	}
}
class Person {
	int r, c; //사람 좌표
	int pr, pc; //편의점
}