반응형

* 핵심 개념

삼성 4월 역량테스트에 나왔던 문제다.

Backtracking으로 구현하면 되지만, 최적화를 하지 않으면 시간이 무지오래걸린다.

우선, 구현 순서에 대해 알아보면

  1. 10*10 맵을 돌면서 1인지 확인한다.
  2. 1이 있다면 5*5 색종이를 덮을 수 있는지 확인한다.
  3. 색종이를 덮을 수 있다면 해당 맵을 0으로 바꾼다.
  4. 색종이를 덮을 수 없다면 더 작은 색종이를 확인한다.

이런 방식으로 진행되는 Backtracking 방식이다.

하지만, 단순하게 조건만 따라 구현하면 시간 초과가 날 수밖에 없다.

이를 위해 다음과 같은 방법을 사용한다.

  1. 매개변수로 row 값을 넘겨준다. 맵을 탐색할 때 row부터 시작하면 이전 줄은 확인하지 않아도 되므로 연산을 줄일 수 있다.    >> int r 파라미터로 사용
  2. 입력 시에 1의 총 개수를 구하고 파라미터에 색종이로 지운 1의 개수를 계산하면서 넘긴다.        >> int total 파라미터로 사용
    • 만약 지운 1의 개수와 입력받은 1의 개수가 같다면 ans에 최소값을 저장하고 return한다.
  3. 큰 색종이부터 들어갈 수 있는지 확인해 보기 때문에, 큰 색종이로 덮을 수 있다면 작은 색종이는 확인해볼 필요 없이 가능하다. >> boolean flag 이용
    • 1*1 색종이까지 확인했는데도 색종이를 넣을 수 없다면 함수를 return한다.
  4. 해당 좌표를 바꾼 후에 아직도 1이 남아있으면 유효하지 않은 경우이므로 return한다.        >> 가지치기

가지치기 때문에 정말 고생이 많았던 문제다.

코드가 깔끔하지는 않지만 누군가에게 도움이 되길 바라며 공유한다.

* 문제

문제

<그림 1>과 같이 정사각형 모양을 한 다섯 종류의 색종이가 있다. 색종이의 크기는 1×1, 2×2, 3×3, 4×4, 5×5로 총 다섯 종류가 있으며, 각 종류의 색종이는 5개씩 가지고 있다.

<그림 1>

색종이를 크기가 10×10인 종이 위에 붙이려고 한다. 종이는 1×1 크기의 칸으로 나누어져 있으며, 각각의 칸에는 0 또는 1이 적혀 있다. 1이 적힌 칸은 모두 색종이로 덮여져야 한다. 색종이를 붙일 때는 종이의 경계 밖으로 나가서는 안되고, 겹쳐도 안된다. 또, 칸의 경계와 일치하게 붙여야 한다. 0이 적힌 칸에는 색종이가 있으면 안된다.

종이가 주어졌을 때, 1이 적힌 모든 칸을 붙이는데 필요한 색종이의 최소 개수를 구해보자.

입력

총 10개의 줄에 종이의 각 칸에 적힌 수가 주어진다.

출력

모든 1을 덮는데 필요한 색종이의 최소 개수를 출력한다. 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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import java.io.*;
import java.util.*;
 
public class Main {
    public static StringTokenizer stk;
    public static StringBuilder sb = new StringBuilder();
    public static int[] paper = {055555};
    public static int[][] map = new int[10][10];
    public static int ans = Integer.MAX_VALUE, one_cnt = 0;
 
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        for (int i = 0; i < 10; i++) {
            stk = new StringTokenizer(br.readLine());
            for (int j = 0; j < 10; j++) {
                map[i][j] = Integer.parseInt(stk.nextToken());
                one_cnt += map[i][j];       //1의 개수 세기
            }
        }
        //r = 해당 row, cnt = 사용한 색종이수, total = 제거한 1의 개수
        Backtracking(000);
        System.out.println(ans == Integer.MAX_VALUE ? -1 : ans);
    }
 
    public static void Backtracking(int r, int cnt, int total) {
        if (ans <= cnt) return;      //현재 값보다 색종이를 많이쓰면 가지치기
        if (total == one_cnt) {      //1을 다 채운 경우
            ans = Math.min(ans, cnt);
            return;
        }
        for (int i = r; i < 10; i++) {     //r부터 시작
            for (int j = 0; j < 10; j++) {
                if (map[i][j] == 1) {
                    boolean flag = false;  //큰 색종이로 덮을 수 있으면 이후 색종이는 확인해 보지 않아도 된다
                    for (int k = 5; k >= 1; k--) {
                        if ((i + k) <= 10 && (j + k) <= 10 && paper[k] > 0) {
                            if (!flag) {
                                flag = check(i, j, k); //k*k 색종이를 덮을 수 있으면 check = true
                            }
                            if (flag) {
                                setVisited(i, j, k);
                                paper[k]--;
                                Backtracking(i, cnt + 1, total + k * k);
                                paper[k]++;
                                setVisited(i, j, k);
                            }
                        }
                    }
                    if (!flag) return;          //색종이를 못덮는 경우
                }
                if (map[i][j] == 1return;     //덮고나서도 해당좌표를 못지우는경우
            }
        }
    }
 
    //size만큼의 색종이를 사용할 수 있는지 확인
    public static boolean check(int r, int c, int size) {
        for (int i = r; i < r + size; i++) {
            for (int j = c; j < c + size; j++) {
                if (map[i][j] == 0return false;
            }
        }
        return true;
    }
 
    //방문한 점을 XOR 연산
    public static void setVisited(int r, int c, int size) {
        for (int i = r; i < r + size; i++) {
            for (int j = c; j < c + size; j++) {
                map[i][j] = map[i][j] ^ 1;
            }
        }
    }
}
cs


반응형
반응형

* 핵심 개념

4월 삼성역테 기출문제이다.

1. 3명의 궁수의 위치를 지정하기 위해 m개 가로열 중 3자리 선택(DFS로 구현)

2. 각 궁수의 위치가 정해지면 3명의 궁수를 BFS 탐색

주의1) 같은 길이면 왼쪽에 있는 몬스터를 먼저 잡기 때문에 dx, dy를 왼쪽, 가운데, 오른쪽 순서대로 해야한다.

주의2) 궁수가 같은 몬스터를 공격할 수도 있기 때문에 찾은 몬스터를 바로 0으로 바꾸지 말것

* 문제

문제

캐슬 디펜스는 성을 향해 몰려오는 적을 잡는 턴 방식의 게임이다. 게임이 진행되는 곳은 크기가 N×M인 격자판으로 나타낼 수 있다. 격자판은 1×1 크기의 칸으로 나누어져 있고, 각 칸에 포함된 적의 수는 최대 하나이다. 격자판의 N번행의 바로 아래(N+1번 행)의 모든 칸에는 성이 있다.

성을 적에게서 지키기 위해 궁수 3명을 배치하려고 한다. 궁수는 성 위의 칸에 배치할 수 있고, 하나의 칸에는 최대 1명의 궁수만 있을 수 있다. 각각의 턴마다 궁수는 적 하나를 공격할 수 있다. 궁수가 공격하는 적은 거리가 D이하인 적 중에서 가장 가까운 적이고, 그러한 적이 여럿일 경우에는 가장 왼쪽에 있는 적을 공격한다. 같은 적이 여러 궁수에게 공격당할 수 있다. 공격받은 적은 게임에서 제외된다. 궁수의 공격이 끝나면, 적이 이동한다. 적은 아래로 한 칸 이동하며, 성이 있는 칸으로 이동한 경우에는 게임에서 제외된다. 모든 적이 격자판에서 제외되면 게임이 끝난다. 

게임 설명에서 보다시피 궁수를 배치한 이후의 게임 진행은 정해져있다. 따라서, 이 게임은 궁수의 위치가 중요하다. 격자판의 상태가 주어졌을 때, 궁수의 공격으로 제거할 수 있는 적의 최대 수를 계산해보자.

격자판의 두 위치 (r1, c2), (r2, c2)의 거리는 |r1-r2| + |c1-c2|이다.

입력

첫째 줄에 격자판 행의 수 N, 열의 수 M, 궁수의 공격 거리 제한 D가 주어진다. 둘째 줄부터 N개의 줄에는 격자판의 상태가 주어진다. 0은 빈 칸, 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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import java.io.*;
import java.util.*;
 
public class Main {
    public static StringTokenizer stk;
    public static StringBuilder sb = new StringBuilder();
    public static int[] dx = {0-10};
    public static int[] dy = {-101};
    public static int n, m, d, ans = 0;
    public static boolean[] used;   //nCr 구할 때 사용하는 배열
    public static int[][] map;
 
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        stk = new StringTokenizer(br.readLine());
        n = Integer.parseInt(stk.nextToken());
        m = Integer.parseInt(stk.nextToken());
        d = Integer.parseInt(stk.nextToken());
        map = new int[n + 1][m];
        used = new boolean[m];
        for (int i = 0; i < n; i++) {
            stk = new StringTokenizer(br.readLine());
            for (int j = 0; j < m; j++) {
                map[i][j] = Integer.parseInt(stk.nextToken());
            }
        }
        nCr(00);
        System.out.println(ans);
    }
 
    //m개의 자리중 3개의 궁수 자리 선택
    public static void nCr(int idx, int cnt) {
        if (cnt == 3) {
            int[][] nmap = new int[n + 1][m];
            for (int i = 0; i < n + 1; i++) {
                for (int j = 0; j < m; j++) {
                    nmap[i][j] = map[i][j];
                }
            }
            bfs(nmap);
            return;
        }
        for (int i = idx; i < m; i++) {
            if (!used[i]) {
                used[i] = true;
                nCr(i, cnt + 1);
                used[i] = false;
            }
        }
    }
 
    public static void bfs(int[][] nmap) {
        int cnt = 0;
        for (int i = n; i > 0; i--) {   //궁수의 포지션을 한칸씩 앞으로 이동
            int archerIdx = 0;
            Queue<Pair> q = new LinkedList<>();
            for (int j = 0; j < m; j++) {
                //nmap에서 궁수 위치는 2
                if (used[j]) {
                    nmap[i][j] = 2;
                    q.add(new Pair(i - 1, j, 1, archerIdx++));
                } else {    //궁수 위치가 아니면 0
                    nmap[i][j] = 0;
                }
            }
 
            boolean[] isEnemyFind = new boolean[3];     //각 궁수가 몬스터를 찾았는지
            boolean[][][] visited = new boolean[n][m][3];   //각 궁수가 해당 좌표를 방문했는지
            boolean[][] isAlreadyFindLocation = new boolean[n][m];  //이미 다른 궁수가 찾은 목표물인지
            ArrayList<Pair> alist = new ArrayList<>();
 
            while (!q.isEmpty()) {
                Pair p = q.poll();
                if (isEnemyFind[p.idx]) continue;
                if (nmap[p.x][p.y] == 1) {
                    isEnemyFind[p.idx] = true;
                    if (!isAlreadyFindLocation[p.x][p.y]) {
                        isAlreadyFindLocation[p.x][p.y] = true;
                        alist.add(p);
                        cnt++;
                    }
                    continue;
                }
                if (!isEnemyFind[p.idx]) {
                    visited[p.x][p.y][p.idx] = true;
                    for (int j = 0; j < 3; j++) {
                        int nx = p.x + dx[j];
                        int ny = p.y + dy[j];
                        if (nx >= 0 && nx < n && ny >= 0 && ny < m && !visited[nx][ny][p.idx] && p.depth < d) {
                            q.add(new Pair(nx, ny, p.depth + 1, p.idx));
                        }
                    }
                }
            }
            for (int j = 0; j < alist.size(); j++) {
                Pair p = alist.get(j);
                nmap[p.x][p.y] = 0;
            }
        }
        ans = Math.max(ans, cnt);
    }
 
    public static class Pair {
        int x, y, depth, idx;
 
        public Pair(int x, int y, int depth, int idx) {
            this.x = x;
            this.y = y;
            this.depth = depth;
            this.idx = idx;
        }
    }
}
cs


반응형
반응형

* 핵심 개념

풀어왔던 삼성 기출문제중에서 BFS, DFS 개념이 필요없는 문제

1. 규칙

이전 방향을 Stack에 저장해서(필자는 String에 붙이는 방식 사용) 뒤에서부터 가져다 쓰는 규칙을 찾으면 쉽게 풀림

ex) 1세대를 움직이는데, 이전 이동한 방향이 3, 0 이라면 다음 이동은 1, 0((3+1)%4)이 된다.

2. 0세대

n번째 세대는 2^n번 움직이지만 0세대는 예외로 그냥 움직인다.

이때는 움직임을 저장하지 않고 현재 x,y 좌표만 변경하면 된다.


* 문제

문제

드래곤 커브는 다음과 같은 세 가지 속성으로 이루어져 있으며, 이차원 좌표 평면 위에서 정의된다. 좌표 평면의 x축은 → 방향, y축은 ↓ 방향이다.

  1. 시작 점
  2. 시작 방향
  3. 세대

0세대 드래곤 커브는 아래 그림과 같은 길이가 1인 선분이다. 아래 그림은 (0, 0)에서 시작하고, 시작 방향은 오른쪽인 0세대 드래곤 커브이다.

... 생략

즉, K(K > 1)세대 드래곤 커브는 K-1세대 드래곤 커브를 끝 점을 기준으로 90도 시계 방향 회전 시킨 다음, 그것을 끝 점에 붙인 것이다.

크기가 100×100인 격자 위에 드래곤 커브가 N개 있다. 이때, 크기가 1×1인 정사각형의 네 꼭짓점이 모두 드래곤 커브의 일부인 정사각형의 개수를 구하는 프로그램을 작성하시오. 격자의 좌표는 (x, y)로 나타내며, 0 ≤ x ≤ 100, 0 ≤ y ≤ 100만 유효한 좌표이다.

입력

첫째 줄에 드래곤 커브의 개수 N(1 ≤ N ≤ 20)이 주어진다. 둘째 줄부터 N개의 줄에는 드래곤 커브의 정보가 주어진다. 드래곤 커브의 정보는 네 정수 x, y, d, g로 이루어져 있다. x와 y는 드래곤 커브의 시작 점, d는 시작 방향, g는 세대이다. (0 ≤ x, y ≤ 100, 0 ≤ d ≤ 3, 0 ≤ g ≤ 10)

입력으로 주어지는 드래곤 커브는 격자 밖으로 벗어나지 않는다. 드래곤 커브는 서로 겹칠 수 있다.

방향은 0, 1, 2, 3 중 하나이고, 다음을 의미한다.

  • 0: x좌표가 증가하는 방향 (→)
  • 1: y좌표가 감소하는 방향 (↑)
  • 2: x좌표가 감소하는 방향 (←)
  • 3: y좌표가 증가하는 방향 (↓)

출력

첫째 줄에 크기가 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
46
47
48
49
50
51
52
import java.io.*;
import java.util.*;
 
public class Main {
    public static StringTokenizer stk;
    public static StringBuilder sb = new StringBuilder();
    public static int[] dx = {10-10};
    public static int[] dy = {0-101};
    public static boolean[][] visited = new boolean[101][101];
 
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int t = Integer.parseInt(br.readLine());
        for (int i = 1; i <= t; i++) {
            stk = new StringTokenizer(br.readLine());
            int x = Integer.parseInt(stk.nextToken());
            int y = Integer.parseInt(stk.nextToken());
            int d = Integer.parseInt(stk.nextToken());
            int g = Integer.parseInt(stk.nextToken());
            String s = String.valueOf(d);
            visited[x][y] = true;
 
            for (int j = 0; j <= g; j++) {
                //1세대마다 2의 제곱으로 늘어남
                if (j != 0 && s.length() >= Math.pow(2, j)) continue;
                for (int k = s.length() - 1; k >= 0; k--) {
                    int nd;
                    if (j == 0) nd = s.charAt(k) - '0';
                    else nd = (s.charAt(k) - '0' + 1) % 4;
 
                    int nx = x + dx[nd];
                    int ny = y + dy[nd];
                    visited[nx][ny] = true;
                    x = nx;
                    y = ny;
                    if (j != 0) s += nd;
                }
            }
        }
        System.out.println(check());
    }
 
    public static int check() {
        int ans = 0;
        for (int i = 0; i < 100; i++) {
            for (int j = 0; j < 100; j++) {
                if (visited[i][j] && visited[i + 1][j] && visited[i][j + 1&& visited[i + 1][j + 1]) ans++;
            }
        }
        return ans;
    }
}
cs


반응형
반응형

* 핵심

1. DFS 를 이용하여 nCr 개의 치킨집을 선택 (순서 상관 x)

# 주의할 점 

nCr을 구할 때 for문을 처음부터 시작하면 시간 초과가 발생한다.

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
// 시간초과 발생
public static void nCr(int cnt) {
    if (cnt == m) {
        dist();
        return;
    }
    for (int i = 1; i <= chickList.size(); i++) {
        if (!chickVisited[i]) {
            chickVisited[i] = true;
            nCr(i, cnt + 1);
            chickVisited[i] = false;
        }
    }
}
 
// 시간 줄이는 nCr
public static void nCr(int idx, int cnt) {
    if (cnt == m) {
        dist();
        return;
    }
    for (int i = idx; i <= chickList.size(); i++) {
        if (!chickVisited[i]) {
            chickVisited[i] = true;
            nCr(i, cnt + 1);
            chickVisited[i] = false;
        }
    }
}
cs

idx값을 추가로 넘겨주고 for문을 idx부터 시작해야 시간을 단축시킬 수 있다.

2. 선택된 치킨집에서 집까지의 단순 좌표값을 빼서 치킨 거리 저장

선택된 치킨집에서 BFS를 사용하여 풀 수 도 있지만, 시간 초과 발생    -> map에 장애물이 없으므로 BFS보단 좌표계산이 훨씬 빠름

* 문제

문제

크기가 N×N인 도시가 있다. 도시는 1×1크기의 칸으로 나누어져 있다. 도시의 각 칸은 빈 칸, 치킨집, 집 중 하나이다. 도시의 칸은 (r, c)와 같은 형태로 나타내고, r행 c열 또는 위에서부터 r번째 칸, 왼쪽에서부터 c번째 칸을 의미한다. r과 c는 1부터 시작한다.

이 도시에 사는 사람들은 치킨을 매우 좋아한다. 따라서, 사람들은 "치킨 거리"라는 말을 주로 사용한다. 치킨 거리는 집과 가장 가까운 치킨집 사이의 거리이다. 즉, 치킨 거리는 집을 기준으로 정해지며, 각각의 집은 치킨 거리를 가지고 있다. 도시의 치킨 거리는 모든 집의 치킨 거리의 합이다.

임의의 두 칸 (r1, c1)과 (r2, c2) 사이의 거리는 |r1-r2| + |c1-c2|로 구한다.

예를 들어, 아래와 같은 지도를 갖는 도시를 살펴보자.

0 2 0 1 0
1 0 1 0 0
0 0 0 0 0
0 0 0 1 1
0 0 0 1 2

0은 빈 칸, 1은 집, 2는 치킨집이다.

(2, 1)에 있는 집과 (1, 2)에 있는 치킨집과의 거리는 |2-1| + |1-2| = 2, (5, 5)에 있는 치킨집과의 거리는 |2-5| + |1-5| = 7이다. 따라서, (2, 1)에 있는 집의 치킨 거리는 2이다.

(5, 4)에 있는 집과 (1, 2)에 있는 치킨집과의 거리는 |5-1| + |4-2| = 6, (5, 5)에 있는 치킨집과의 거리는 |5-5| + |4-5| = 1이다. 따라서, (5, 4)에 있는 집의 치킨 거리는 1이다.

이 도시에 있는 치킨집은 모두 같은 프랜차이즈이다. 프렌차이즈 본사에서는 수익을 증가시키기 위해 일부 치킨집을 폐업시키려고 한다. 오랜 연구 끝에 이 도시에서 가장 수익을 많이 낼 수 있는  치킨집의 개수는 최대 M개라는 사실을 알아내었다.

도시에 있는 치킨집 중에서 최대 M개를 고르고, 나머지 치킨집은 모두 폐업시켜야 한다. 어떻게 고르면, 도시의 치킨 거리가 가장 작게 될지 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N(2 ≤ N ≤ 50)과 M(1 ≤ M ≤ 13)이 주어진다.

둘째 줄부터 N개의 줄에는 도시의 정보가 주어진다.

도시의 정보는 0, 1, 2로 이루어져 있고, 0은 빈 칸, 1은 집, 2는 치킨집을 의미한다. 집의 개수는 2N개를 넘지 않으며, 적어도 1개는 존재한다. 치킨집의 개수는 M보다 크거나 같고, 13보다 작거나 같다.

출력

첫째 줄에 폐업시키지 않을 치킨집을 최대 M개를 골랐을 때, 도시의 치킨 거리의 최솟값을 출력한다.


* 소스 코드

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import java.io.*;
import java.util.*;
 
public class Main {
    public static StringTokenizer stk;
    public static StringBuilder sb = new StringBuilder();
    public static int n, m, ans = Integer.MAX_VALUE;
    public static int[][] map;  //지도
    public static boolean[] chickVisited;       //nCr에서 방문한 치킨집
    public static ArrayList<Pair> homeList = new ArrayList<>();     //집 위치
    public static ArrayList<Pair> chickList = new ArrayList<>();    //치킨집 위치
 
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        stk = new StringTokenizer(br.readLine());
        n = Integer.parseInt(stk.nextToken());
        m = Integer.parseInt(stk.nextToken());
        map = new int[n + 1][n + 1];
        for (int i = 1; i <= n; i++) {
            stk = new StringTokenizer(br.readLine());
            for (int j = 1; j <= n; j++) {
                map[i][j] = Integer.parseInt(stk.nextToken());
                if (map[i][j] == 1) {
                    homeList.add(new Pair(i, j));
                } else if (map[i][j] == 2) {
                    chickList.add(new Pair(i, j));
                }
            }
        }
        chickVisited = new boolean[chickList.size() + 1];
 
        nCr(10);
        System.out.println(ans);
    }
 
    public static void nCr(int idx, int cnt) {
        if (cnt == m) {
            dist();
            return;
        }
        for (int i = idx; i <= chickList.size(); i++) {
            if (!chickVisited[i]) {
                chickVisited[i] = true;
                nCr(i, cnt + 1);
                chickVisited[i] = false;
            }
        }
    }
 
    public static void dist() {
        int sum = 0;
        for (int i = 0; i < homeList.size(); i++) {
            Pair homeInfo = homeList.get(i);
            int tmp = Integer.MAX_VALUE;
            for (int j = 1; j < chickVisited.length; j++) {
                if (chickVisited[j]) {
                    Pair chickInfo = chickList.get(j - 1);
                    int chickDist = Math.abs(homeInfo.x - chickInfo.x) + Math.abs(homeInfo.y - chickInfo.y);
                    tmp = Math.min(tmp, chickDist);
                }
            }
            sum += tmp;
        }
        ans = Math.min(ans, sum);
    }
 
    public static class Pair {
        int x, y;
 
        public Pair(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }
}
cs


반응형
반응형

* 핵심 개념

서류 등수나 면접 등수중 하나만 높으면 뽑을 수 있다.

서류 등수 순으로 정렬하고, 면접 등수의 최소값을 계속 비교하면서 cnt를 증가하면 된다.

* 문제

문제

언제나 최고만을 지향하는 굴지의 대기업 진영 주식회사가 신규 사원 채용을 실시한다. 인재 선발 시험은 1차 서류심사와 2차 면접시험으로 이루어진다. 최고만을 지향한다는 기업의 이념에 따라 그들은 최고의 인재들만을 사원으로 선발하고 싶어 한다.

그래서 진영 주식회사는, 다른 모든 지원자와 비교했을 때 서류심사 성적과 면접시험 성적 중 적어도 하나가 다른 지원자보다 떨어지지 않는 자만 선발한다는 원칙을 세웠다. 즉, 어떤 지원자 A의 성적이 다른 어떤 지원자 B의 성적에 비해 서류 심사 결과와 면접 성적이 모두 떨어진다면 A는 결코 선발되지 않는다.

이러한 조건을 만족시키면서, 진영 주식회사가 이번 신규 사원 채용에서 선발할 수 있는 신입사원의 최대 인원수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에는 테스트 케이스의 개수 T(1 ≤ T ≤ 20)가 주어진다. 각 테스트 케이스의 첫째 줄에 지원자의 숫자 N(1 ≤ N ≤ 100,000)이 주어진다. 둘째 줄부터 N개 줄에는 각각의 지원자의 서류심사 성적, 면접 성적의 순위가 공백을 사이에 두고 한 줄에 주어진다. 두 성적 순위는 모두 1위부터 N위까지 동석차 없이 결정된다고 가정한다.

출력

각 테스트 케이스에 대해서 진영 주식회사가 선발할 수 있는 신입사원의 최대 인원수를 한 줄에 하나씩 출력한다.

* 소스 코드

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
import java.io.*;
import java.util.*;
 
public class Main {
    public static StringTokenizer stk;
    public static StringBuilder sb = new StringBuilder();
 
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int t = Integer.parseInt(br.readLine());
        for (int test = 1; test <= t; test++) {
            int n = Integer.parseInt(br.readLine());
            Pair[] pair = new Pair[n];
 
            for (int i = 0; i < n; i++) {
                stk = new StringTokenizer(br.readLine());
                int x = Integer.parseInt(stk.nextToken());
                int y = Integer.parseInt(stk.nextToken());
                pair[i] = new Pair(x, y);
            }
            Arrays.sort(pair, new Comparator<Pair>() {
                @Override
                public int compare(Pair o1, Pair o2) {
                    return o1.x - o2.x;
                }
            });
            int min = pair[0].y;
            int cnt = 1;
            for (int i = 1; i < n; i++) {
                if (pair[i].y < min) {
                    min = pair[i].y;
                    cnt++;
                }
            }
            sb.append(cnt + "\n");
        }
        System.out.println(sb);
    }
 
cs


반응형
반응형

* 핵심

삼성기출중 쉬운편에 속하는 문제이다.

2중 For문을 돌면서 BFS로 탐색하면 된다.

문제에서 설명이 조금 애매한데, 몇 번이라기보다는 몇 일이 걸린다고 이해하는게 편하다.

하루에 인구 이동한 나라는 그 날은 또 다른 나라랑 연합을 맺을 수 없다.

문제에 주어진 마지막 예시를 보면,

1일차

2일차

3일차

위 예제를 보면 6번의 인구 이동이 있지만 3일에 걸쳐 인구 이동되었기 때문에 3을 출력하는 것이 맞다.

* 문제

문제

N×N크기의 땅이 있고, 땅은 1×1개의 칸으로 나누어져 있다. 각각의 땅에는 나라가 하나씩 존재하며, r행 c열에 있는 나라에는 A[r][c]명이 살고 있다. 인접한 나라 사이에는 국경선이 존재한다. 모든 나라는 1×1 크기이기 때문에, 모든 국경선은 정사각형 형태이다.

오늘부터 인구 이동이 시작되는 날이다.

인구 이동은 다음과 같이 진행되고, 더 이상 아래 방법에 의해 인구 이동이 없을 때까지 지속된다.

  • 국경선을 공유하는 두 나라의 인구 차이가 L명 이상, R명 이하라면, 두 나라가 공유하는 국경선을 오늘 하루동안 연다.
  • 위의 조건에 의해 열어야하는 국경선이 모두 열렸다면, 인구 이동을 시작한다.
  • 국경선이 열려있어 인접한 칸만을 이용해 이동할 수 있으면, 그 나라를 오늘 하루 동안은 연합이라고 한다.
  • 연합을 이루고 있는 각 칸의 인구수는 (연합의 인구수) / (연합을 이루고 있는 칸의 개수)가 된다. 편의상 소수점은 버린다.
  • 연합을 해체하고, 모든 국경선을 닫는다.

각 나라의 인구수가 주어졌을 때, 인구 이동이 몇 번 발생하는지 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N, L, R이 주어진다. (1 ≤ N ≤ 50, 1 ≤ L ≤ R ≤ 100)

둘째 줄부터 N개의 줄에 각 나라의 인구수가 주어진다. r행 c열에 주어지는 정수는 A[r][c]의 값이다. (1 ≤ A[r][c] ≤ 100)

인구 이동이 발생하는 횟수가 2,000번 보다 작거나 같은 입력만 주어진다.

출력

인구 이동이 몇 번 발생하는지 첫째 줄에 출력한다.

* 소스 코드

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import java.io.*;
import java.util.*;
 
public class Main {
    public static StringTokenizer stk;
    public static StringBuilder sb = new StringBuilder();
    public static int[] dx = {1-100};
    public static int[] dy = {001-1};
    public static int min, max, ans = 0;
 
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        stk = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(stk.nextToken());
        min = Integer.parseInt(stk.nextToken());
        max = Integer.parseInt(stk.nextToken());
        int[][] map = new int[n + 1][n + 1];
        boolean[][] visited = new boolean[n + 1][n + 1];
        for (int i = 1; i <= n; i++) {
            stk = new StringTokenizer(br.readLine());
            for (int j = 1; j <= n; j++) {
                map[i][j] = Integer.parseInt(stk.nextToken());
            }
        }
        boolean hasMoreMoving = false;  //while문에서 인구이동 발생 여부 확인
        while (!hasMoreMoving) {
            Queue<Pair> q = new LinkedList<>();
            int sum = 0, cnt = 0;                           //sum = 인구 이동할 나라의 사람수, cnt = 인구 이동한 나라 개수
            ArrayList<Pair> alist = new ArrayList<>();      //해당 점을 방문했을 때 이동한 점의 좌표 정보
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    q.add(new Pair(i, j));
                    while (!q.isEmpty()) {
                        Pair p = q.poll();
                        if (!visited[p.x][p.y]) {
                            visited[p.x][p.y] = true;
                            cnt++;
                            sum += map[p.x][p.y];
                            alist.add(new Pair(p.x, p.y));
                            for (int k = 0; k < 4; k++) {
                                int nx = p.x + dx[k];
                                int ny = p.y + dy[k];
                                if (nx > 0 && nx <= n && ny > 0 && ny <= n && !visited[nx][ny] && checkPerson(map[p.x][p.y], map[nx][ny])) {
                                    q.add(new Pair(nx, ny));
                                }
                            }
                        }
                    }
 
                    //방문한 좌표가 인구 이동이 필요한 경우
                    if (alist.size() > 1) {
                        int movingSum = sum / cnt;
                        for (int k = 0; k < alist.size(); k++) {
                            Pair p = alist.get(k);
                            map[p.x][p.y] = movingSum;
                        }
                        hasMoreMoving = true;
                    }
                    //매 시작점마다 초기화
                    alist = new ArrayList<>();
                    sum = 0;
                    cnt = 0;
                }
            }   //end of for
            if (hasMoreMoving) {        //더이상 움직일 게 있다면 visited배열 초기화
                ans++;
                hasMoreMoving = false;
                visited = new boolean[n + 1][n + 1];
            } else break;
        }
        System.out.println(ans);
    }
 
    //현재 인구와 이동할 인구의 차이 계산
    public static boolean checkPerson(int curCnt, int nextCnt) {
        int absCnt = Math.abs(curCnt - nextCnt);
        if (absCnt >= min && absCnt <= max) return true;
        else return false;
    }
 
    public static class Pair {
        int x, y;
 
        public Pair(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }
}
cs


반응형
반응형

* 핵심 개념

삽질을 상당히 많이한 문제다.

문제를 해석하면 한 방향으로 기울이면 끝까지 이동해야하고 두 구슬이 같은 위치인 경우도 처리가 필요하다.

4%에서 계속 틀렸다고 나왔는데 이유를 알고보니 개념의 실수였다.

구슬의 방문여부를 4차원 배열로 해결해야 하는데, 2차원배열 2개로 해결하려다 보니 당연히 틀릴수밖에 없었다.

이 둘의 차이는 명백하니 실수하지 말아야겠다.


문제 해결 순서는 

빨간 구슬을 한 방향 끝까지 이동 >> 파란 구슬을 한 방향 끝까지 이동 >> 둘이 같은 점이면 많이 움직인 구슬을 한칸 되돌림 >> 새로운 점 큐에 추가


* 문제

문제

스타트링크에서 판매하는 어린이용 장난감 중에서 가장 인기가 많은 제품은 구슬 탈출이다. 구슬 탈출은 직사각형 보드에 빨간 구슬과 파란 구슬을 하나씩 넣은 다음, 빨간 구슬을 구멍을 통해 빼내는 게임이다.

보드의 세로 크기는 N, 가로 크기는 M이고, 편의상 1×1크기의 칸으로 나누어져 있다. 가장 바깥 행과 열은 모두 막혀져 있고, 보드에는 구멍이 하나 있다. 빨간 구슬과 파란 구슬의 크기는 보드에서 1×1크기의 칸을 가득 채우는 사이즈이고, 각각 하나씩 들어가 있다. 게임의 목표는 빨간 구슬을 구멍을 통해서 빼내는 것이다. 이때, 파란 구슬이 구멍에 들어가면 안된다.

이때, 구슬을 손으로 건드릴 수는 없고, 중력을 이용해서 이리 저리 굴려야 한다. 왼쪽으로 기울이기, 오른쪽으로 기울이기, 위쪽으로 기울이기, 아래쪽으로 기울이기와 같은 네 가지 동작이 가능하다.

각각의 동작에서 공은 동시에 움직인다. 빨간 구슬이 구멍에 빠지면 성공이지만, 파란 구슬이 구멍에 빠지면 실패이다. 빨간 구슬과 파란 구슬이 동시에 구멍에 빠져도 실패이다. 빨간 구슬과 파란 구슬은 동시에 같은 칸에 있을 수 없다. 또, 빨간 구슬과 파란 구슬의 크기는 한 칸을 모두 차지한다. 기울이는 동작을 그만하는 것은 더 이상 구슬이 움직이지 않을 때 까지이다.

보드의 상태가 주어졌을 때, 최소 몇 번 만에 빨간 구슬을 구멍을 통해 빼낼 수 있는지 구하는 프로그램을 작성하시오.

입력

첫 번째 줄에는 보드의 세로, 가로 크기를 의미하는 두 정수 N, M (3 ≤ N, M ≤ 10)이 주어진다. 다음 N개의 줄에 보드의 모양을 나타내는 길이 M의 문자열이 주어진다. 이 문자열은 '.', '#', 'O', 'R', 'B' 로 이루어져 있다. '.'은 빈 칸을 의미하고, '#'은 공이 이동할 수 없는 장애물 또는 벽을 의미하며, 'O'는 구멍의 위치를 의미한다. 'R'은 빨간 구슬의 위치, 'B'는 파란 구슬의 위치이다.

입력되는 모든 보드의 가장자리에는 모두 '#'이 있다. 구멍의 개수는 한 개 이며, 빨간 구슬과 파란 구슬은 항상 1개가 주어진다.

출력

최소 몇 번 만에 빨간 구슬을 구멍을 통해 빼낼 수 있는지 출력한다. 만약, 10번 이하로 움직여서 빨간 구슬을 구멍을 통해 빼낼 수 없으면 -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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import java.io.*;
import java.util.*;
 
public class Main {
    public static StringTokenizer stk;
    public static StringBuilder sb = new StringBuilder();
    public static int[] dx = {1-100};
    public static int[] dy = {001-1};
    public static int n, m;
    public static int rx, ry, bx, by;
    public static int[][] map;
    public static boolean[][][][] visited;
    public static Queue<Pair> q = new LinkedList<>();
 
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        stk = new StringTokenizer(br.readLine());
        n = Integer.parseInt(stk.nextToken());
        m = Integer.parseInt(stk.nextToken());
        map = new int[n + 1][m + 1];
        visited = new boolean[n + 1][m + 1][n + 1][m + 1];
        for (int i = 1; i <= n; i++) {
            String s = br.readLine();
            for (int j = 1; j <= s.length(); j++) {
                int tmp = 0;
                switch (s.charAt(j - 1)) {
                    case '#':
                        tmp = -1;
                        break;
                    case '.':
                        tmp = 0;
                        break;
                    case 'R':
                        tmp = 1;
                        rx = i;
                        ry = j;
                        break;
                    case 'B':
                        tmp = 2;
                        bx = i;
                        by = j;
                        break;
                    case 'O':
                        tmp = 3;
                        break;
                }
                map[i][j] = tmp;
            }
        }
        System.out.println(bfs());
    }
 
    public static int bfs() {
        q.add(new Pair(rx, ry, bx, by, 0));
        visited[rx][ry][bx][by] = true;
        while (!q.isEmpty()) {
            Pair p = q.poll();
            if (p.cnt > 10continue;
            if (map[p.bx][p.by] == 3continue;
            if (map[p.rx][p.ry] == 3return p.cnt;
 
            for (int i = 0; i < 4; i++) {
                //빨간 구슬 상하좌우 끝까지 이동
                int next_rx = p.rx, next_ry = p.ry;
                while (true) {
                    //다음 지점이 벽이랑 구멍이 아니면
                    if (map[next_rx][next_ry] != -1 && map[next_rx][next_ry] != 3) {
                        next_rx += dx[i];
                        next_ry += dy[i];
                    } else {
                        //다음 지점이 벽이면
                        if (map[next_rx][next_ry] == -1) {
                            next_rx -= dx[i];
                            next_ry -= dy[i];
                        }
                        break;
                    }
                }
 
                //파란 구슬 상하좌우 끝까지 이동
                int next_bx = p.bx, next_by = p.by;
                while (true) {
                    //다음 지점이 벽이랑 구멍이 아니면
                    if (map[next_bx][next_by] != -1 && map[next_bx][next_by] != 3) {
                        next_bx += dx[i];
                        next_by += dy[i];
                    } else {
                        //다음 지점이 벽이면
                        if (map[next_bx][next_by] == -1) {
                            next_bx -= dx[i];
                            next_by -= dy[i];
                        }
                        break;
                    }
                }
 
                //구한 Red, Blue 의 점이 서로 같은데 'O'가 아닌 경우
                //더 움직인 구슬의 dx[i], dy[i]를 빼준다
                if (next_rx == next_bx && next_ry == next_by) {
                    if (map[next_rx][next_ry] != 3) {
                        int red_cost = Math.abs(next_rx - p.rx) + Math.abs(next_ry - p.ry);
                        int blue_cost = Math.abs(next_bx - p.bx) + Math.abs(next_by - p.by);
                        if (red_cost > blue_cost) {
                            next_rx -= dx[i];
                            next_ry -= dy[i];
                        } else {
                            next_bx -= dx[i];
                            next_by -= dy[i];
                        }
                    }
                }
 
                //next 점이 방문한적 없다면 큐에 추가
                if (!visited[next_rx][next_ry][next_bx][next_by]) {
                    visited[next_rx][next_ry][next_bx][next_by] = true;
                    q.add(new Pair(next_rx, next_ry, next_bx, next_by, p.cnt + 1));
                }
            }
        }
        return -1;
    }
 
    public static class Pair {
        int rx, ry, bx, by, cnt;
 
        public Pair(int rx, int ry, int bx, int by, int cnt) {
            this.rx = rx;
            this.ry = ry;
            this.bx = bx;
            this.by = by;
            this.cnt = cnt;
        }
    }
}
cs


반응형
반응형

* 핵심 개념

고려해야 할 사항은 크게 두가지다.

1. 물고기가 같은 거리에 있는 경우 위에 있고, 왼쪽에 있는 물고기를 먹는다. 

다른 방법도 존재하지만 우선순위 큐를 사용하여 처리하였다. (148ms)

먹이를 찾았을 때 큐에 남은 점들을 다시 확인해서 비교한다. (80ms)

2. 물고기를 먹고 나서 상어 크기가 증가하는 지 확인해야 한다.

하나 주의해야 할 점은 상어의 위치를 받고 map을 0으로 변경해주는 작업도 필요하다.


요약하면, 

상어 위치 확인  >> 물고기 탐색(BFS) >> 찾은 물고기 우선순위 큐에 저장 >> 우선순위 큐의 맨 앞 물고기 선택 >> 상어 위치 변경 및 상어 크기 증가여부 확인

상어 위치 확인 >> 물고기 탐색(BFS) >> 물고기 찾으면 남아있는 큐 탐색 >> 최적의 물고기 위치 확인 >> 상어 위치 변경 및 상어 크기 증가 여부 확인

위 순서대로 진행하며 BFS가 끝낫는데 큐의 크기가 0이라면 먹을 수 있는 물고기가 없다는 뜻이므로 움직인 거리를 출력하면 된다.

* 문제

문제

N×N 크기의 공간에 물고기 M마리와 아기 상어 1마리가 있다. 공간은 1×1 크기의 정사각형 칸으로 나누어져 있다. 한 칸에는 물고기가 최대 1마리 존재한다.

아기 상어와 물고기는 모두 크기를 가지고 있고, 이 크기는 자연수이다. 가장 처음에 아기 상어의 크기는 2이고, 아기 상어는 1초에 상하좌우로 인접한 한 칸씩 이동한다.

아기 상어는 자신의 크기보다 큰 물고기가 있는 칸은 지나갈 수 없고, 나머지 칸은 모두 지나갈 수 있다. 아기 상어는 자신의 크기보다 작은 물고기만 먹을 수 있다. 따라서, 크기가 같은 물고기는 먹을 수 없지만, 그 물고기가 있는 칸은 지나갈 수 있다.

아기 상어가 어디로 이동할지 결정하는 방법은 아래와 같다.

  • 더 이상 먹을 수 있는 물고기가 공간에 없다면 아기 상어는 엄마 상어에게 도움을 요청한다.
  • 먹을 수 있는 물고기가 1마리라면, 그 물고기를 먹으러 간다.
  • 먹을 수 있는 물고기가 1마리보다 많다면, 거리가 가장 가까운 물고기를 먹으러 간다.
    • 거리는 아기 상어가 있는 칸에서 물고기가 있는 칸으로 이동할 때, 지나야하는 칸의 개수의 최소값이다.
    • 거리가 가까운 물고기가 많다면, 가장 위에 있는 물고기, 그러한 물고기가 여러마리라면, 가장 왼쪽에 있는 물고기를 먹는다.

아기 상어의 이동은 1초 걸리고, 물고기를 먹는데 걸리는 시간은 없다고 가정한다. 즉, 아기 상어가 먹을 수 있는 물고기가 있는 칸으로 이동했다면, 이동과 동시에 물고기를 먹는다. 물고기를 먹으면, 그 칸은 빈 칸이 된다.

아기 상어는 자신의 크기와 같은 수의 물고기를 먹을 때 마다 크기가 1 증가한다. 예를 들어, 크기가 2인 아기 상어는 물고기를 2마리 먹으면 크기가 3이 된다.

공간의 상태가 주어졌을 때, 아기 상어가 몇 초 동안 엄마 상어에게 도움을 요청하지 않고 물고기를 잡아먹을 수 있는지 구하는 프로그램을 작성하시오.

입력

첫째 줄에 공간의 크기 N(2 ≤ N ≤ 20)이 주어진다.

둘째 줄부터 N개의 줄에 공간의 상태가 주어진다. 공간의 상태는 0, 1, 2, 3, 4, 5, 6, 9로 이루어져 있고, 아래와 같은 의미를 가진다.

  • 0: 빈 칸
  • 1, 2, 3, 4, 5, 6: 칸에 있는 물고기의 크기
  • 9: 아기 상어의 위치

아기 상어는 공간에 한 마리 있다.

출력

첫째 줄에 아기 상어가 엄마 상어에게 도움을 요청하지 않고 물고기를 잡아먹을 수 있는 시간을 출력한다.

* 소스 코드

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import java.io.*;
import java.util.*;
 
public class Main {
    public static StringTokenizer stk;
    public static StringBuilder sb = new StringBuilder();
    public static int[] dx = {-1001};
    public static int[] dy = {0-110};
    public static int n, eating = 0;
 
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        n = Integer.parseInt(br.readLine());
        int[][] map = new int[n][n];
        Queue<Shark> q = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            stk = new StringTokenizer(br.readLine());
            for (int j = 0; j < n; j++) {
                map[i][j] = Integer.parseInt(stk.nextToken());
                if (map[i][j] == 9) {
                    q.add(new Shark(i, j, 20));
                    map[i][j] = 0;
                }
            }
        }
 
        int ans = 0;
        boolean[][] visited = new boolean[n][n];
        while (!q.isEmpty()) {
            Shark shark = q.poll();
            if (!visited[shark.x][shark.y]) {
                visited[shark.x][shark.y] = true;
                if (map[shark.x][shark.y] != 0 && map[shark.x][shark.y] < shark.power) {  //상어가 먹이를 찾으면
                    Shark curr = shark;
                    //거리 > 높이 > 왼쪽 순서로 먹기 위해 남은 Queue 탐색
                    while (!q.isEmpty()) {
                        shark = q.poll();
                        if (map[shark.x][shark.y] != 0 && map[shark.x][shark.y] < shark.power) {
                            if (curr.cost == shark.cost) {
                                if (curr.x > shark.x) {
                                    curr = shark;
                                } else if (curr.x == shark.x && curr.y > shark.y) {
                                    curr = shark;
                                }
                            } else if (curr.cost > shark.cost) {
                                curr = shark;
                            }
                        }
                    }
                    map[curr.x][curr.y] = 0;
                    eating++;
                    ans += curr.cost;
                    int flag = 0;
                    if (eating == curr.power) {        //상어가 성장할 수 있으면
                        flag = 1;
                        eating = 0;
                    }
                    q.clear();
                    q.add(new Shark(curr.x, curr.y, curr.power + flag, 0));
                    visited = new boolean[n][n];
                    continue;
                }
                for (int i = 0; i < 4; i++) {
                    int nx = shark.x + dx[i];
                    int ny = shark.y + dy[i];
                    if (nx >= 0 && nx < n && ny >= 0 && ny < n && map[nx][ny] <= shark.power && !visited[nx][ny]) {
                        q.add(new Shark(nx, ny, shark.power, shark.cost + 1));
                    }
                }
            }
        }
        System.out.println(ans);
    }
 
    public static class Shark {
        int x, y, power, cost;
 
        public Shark(int x, int y, int power, int cost) {
            this.x = x;
            this.y = y;
            this.power = power;
            this.cost = cost;
        }
    }
}
cs


반응형