백준 2564 - 경비원

Updated:

Java

2564 번 - 경비원

문제

블록의 크기와 상점의 개수 및 위치 그리고 동근이의 위치가 주어질 때 동근이의 위치와 각 상점 사이의 최단 거리의 합을 구하는 프로그램을 작성하시오.

접근 방법

처음에는 문제에서 제시한 대로 동근이의 처음 위치에서 시계방향으로 탐색하여 상점들을 찾는다.
찾은 상점까지의 거리와 직사각형 둘레의 길이를 뺀 값 중, 짧은 값을 상점까지 최소 거리로 하여 정답을 구하였다.

두번째로는 동원이와 상점의 위치를 파악하여, 각 경우의 수대로 조건문을 통해 거리를 구하였다.
동원과 상점의 위치가, 수직으로 위치하면 ex) (1 , 3) or (2 , 3) 두 x와 y의 차이가 최단 거리이고
수평으로 위치하면 ex) (1,2) or (3,4) or (1,1) [현재 위치의 수직의 값 + 각 점까지의 거리의 차이(같은 라인일 때) or 합(다른 라인일 때)]이 최단거리가 된다.

코드

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

public class Main {
	
	static int n, m, result;
	static int dir, diff, y, x;
    public static void main(String []args) throws IOException {        
    	BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    	StringTokenizer stk = new StringTokenizer(br.readLine());
    	m = stoi(stk.nextToken());
    	n = stoi(stk.nextToken());
    	
    	int k = stoi(br.readLine());
    	
    	ArrayList<int[]> loc = new ArrayList<>();
    	for(int i = 0; i < k; i++) {
    		stk = new StringTokenizer(br.readLine());
    		loc.add(new int[] {stoi(stk.nextToken()), stoi(stk.nextToken())} );
    	}
    	
    	stk = new StringTokenizer(br.readLine());
    	dir = stoi(stk.nextToken());		// 동근이의 방향
    	diff = stoi(stk.nextToken());
    	
    	int[] location = point(dir,diff);
    	y = location[0];	// 동근이의 처음 위치
    	x = location[1];

		// 각 상점의 값을 주어, 최단 거리를 구한다.
    	for(int[] arr : loc) {
    		result += rollin(arr);
    	}
    	System.out.println(result);
    	br.close();
    }

	// 시계 방향 회전
    static int[] dy = {-1,0,1,0};
    static int[] dx = {0,1,0,-1};

    // 왼쪽부터, 하, 우, 좌, 상
    private static int rollin(int[] arr) {
		int count = 0;
		int loopIdx = 0;
		
		// 현재 동근이의 위치에 따라 처음 도는 방향이 정해진다
		if(dir == 1)
			loopIdx = 1;
		else if(dir == 2)
			loopIdx = 3;
		else if(dir == 3)
			loopIdx = 0;
		else if(dir == 4)
			loopIdx = 2;
		
		int curY = y;
		int curX = x;
		
		// 목표 상점의 위치
		int[] temp = point(arr[0], arr[1]);
		int targetY = temp[0];
		int targetX = temp[1];
		
		// 시계 방향으로 목표 상점을 만날 때 까지 탐색을 시작한다.
		while(true) {
			count++;
			curY += dy[loopIdx];
			curX += dx[loopIdx];
			
			// 직선으로 가다가 맵 밖으로 나가면, 방향을 바꾸어 시계 방향으로 돈다.
			if(!isIn(curY, curX)) {
				curY -= dy[loopIdx];
				curX -= dx[loopIdx];
				loopIdx = (loopIdx + 1) % 4;
				curY += dy[loopIdx];
				curX += dx[loopIdx];
			}
			if(curY == targetY && curX == targetX)
				break;
		}
		
		count = Math.min(count, 2 * n + 2 * m - count);
		return count;
	}
    
    // 주어진 범위 안에 있는가
    public static boolean isIn(int y, int x){
        if(y < 0 || y > n || x < 0 || x > m)
            return false;
        return true;
    }
    
    // 방향과 떨어진 거리를 입력 받아, 좌표를 반환한다.
    private static int[] point(int a, int b) {
    	switch(a) {
    	case 1:
    		return new int[] {0, b};
    	case 2:
    		return new int[] {n, b};
    	case 3:
    		return new int[] {b, 0};
    	case 4:
    		return new int[] {b, m};
    	}
    	return null;
    }
    
    static int stoi(String str) {
    	return Integer.parseInt(str);
    }
}

코드 - 2

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

public class Main {
	
	static int n, m, result;
	static int dir, diff, y, x;
    public static void main(String []args) throws IOException {        
    	BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    	StringTokenizer stk = new StringTokenizer(br.readLine());
    	m = stoi(stk.nextToken());
    	n = stoi(stk.nextToken());
    	
    	int k = stoi(br.readLine());
    	
    	ArrayList<int[]> loc = new ArrayList<>();
    	for(int i = 0; i < k; i++) {
    		stk = new StringTokenizer(br.readLine());
    		loc.add(new int[] {stoi(stk.nextToken()), stoi(stk.nextToken())} );
    	}
    	
    	stk = new StringTokenizer(br.readLine());
    	dir = stoi(stk.nextToken());		// 동근이의 방향
    	diff = stoi(stk.nextToken());
    	
    	int[] location = point(dir,diff);
    	y = location[0];	// 동근이의 처음 위치
    	x = location[1];
    	
    	int sy, sx, sdir;
    	for(int[] arr : loc) {
    		sdir = arr[0];
    		location = point(sdir, arr[1]);
    		sy = location[0];
    		sx = location[1];
    		int min = 0;
    		
    		// 수직으로 위치하여 있으면
    		if((dir <= 2 && sdir >= 3) || (dir >= 3 && sdir <= 2)) {
    			result += Math.abs(y - sy) + Math.abs(x - sx);
    		}
    		// 수평으로 위치하여 있으면
    		else if(dir <= 2 && sdir <= 2){
    			if(dir != sdir)		// 같은 라인에 있지 않으면
    				min = sx + x;
    			else
    				min = Math.abs(sx - x);
    			result += Math.abs(sy - y) + Math.min(min, 2 * m - min);
    		}
    		else if(dir >= 3 && sdir >= 3){
    			if(dir != sdir)
    				min = sy + y;
    			else
    				min = Math.abs(sy - y);
    			result += Math.abs(sx - x) + Math.min(min, 2 * n - min);
    		}
    		
    	}
    	System.out.println(result);
    	br.close();
    }

    
    // 주어진 범위 안에 있는가
    public static boolean isIn(int y, int x){
        if(y < 0 || y > n || x < 0 || x > m)
            return false;
        return true;
    }
    
    // 방향과 떨어진 거리를 입력 받아, 좌표를 반환한다.
    private static int[] point(int a, int b) {
    	switch(a) {
    	case 1:
    		return new int[] {0, b};
    	case 2:
    		return new int[] {n, b};
    	case 3:
    		return new int[] {b, 0};
    	case 4:
    		return new int[] {b, m};
    	}
    	return null;
    }
    
    static int stoi(String str) {
    	return Integer.parseInt(str);
    }
}

총평

후기

처음 접근 하였을 때, 쉬운 문제였음에도 불구하고 접근하는 과정에서 갈팡질팡 하여 오래 걸려 자존심에 스크래치가 난 문제이다.
멘탈 관리 잘하자

개선할 점

없습니다.