반응형
## < CJ 인적성(CJAT) 후기 >

- 평소 쉽다고 소문난 CJ 인적성이지만 이번에는 예시문제 4개만 주고 아무런 정보를 주지 않았다.
- 기사를 찾아봐도 평소 CJ 문제집으로 공부하다가는 큰코다친다 이런식으로 나와있길래
- GSAT처럼 나올 것 같은 느낌이라 20대인적성 문제를 풀면서 대비했다.

## < CJAT 시험 정보 >
- GSAT처럼 단계별 풀고 넘어가는 방식

1. 1 교시 25문제 30분 (독해, 요약 15문제 + 언어 추리 15문제)
2. 2교시 15문제 15분 (글의 구조 순서 15문제)
3. 3교시 25문제 35분 (수리 25문제)
4. 4교시 15문제 20분 (신유형 도형돌리기 15문제)

- 다른건 둘째치고 4교시가 LG 도형돌리기와 비슷하지만 차이점이 있다.
- 신유형의 경우 예시문제에 규칙이 주어진다. ex) ㅇ3 => 시계방항 270도 회전, ㅁ3 => 도형 내부 270도 회전 ..
- 이런식으로 순서도에 따라 이동하면서 회전을 다 수행했을 때 결과를 찾으면 된다.
- 곧 유형 분석법이 나오겠지만 개인적으로 쉽게 푸는 방법은 보기에 전부 들어가 있는 도형 하나를 잡고 이걸 조건에 따라 같이 돌려보면 훨씬 수월하게 문제를 풀 수 있다.
- 총 못푼문제는 20문제 가량 되는거 같은데 결과는 나와봐야 알 것 같다.

## < 결과 >

반응형
반응형


## < 11번가 인턴 SKCT 및 코딩테스트 후기 >
다른 서류는 잘안붙는데 체험형 인턴이라 그런가 붙었다.

제출란에 포트폴리오가 선택이였는데 몇개 추가해서 제출한 덕인거같은 느낌..?

## < SKCT 후기 >
처음 보는 SKCT였다.

듣기로는 2019 상반기 공채랑 문제가 똑같다고 하던데 본인은 처음이라 잘 모르겠다.

총 인원 : 71명

응시 : 40명

불참 : 31명

개발 직군만 불참이 거의 절반가까이 된다.

아마도 체험형이고, 다른 기업 시험이 있기 때문에 겹쳐서 대부분 포기한 것이라 생각한다.

교재는 위포트를 사용했는데, 실제 시험장에서는 위포트보다 쉬웠다.

### 실행 역량

유튜브 보면서 공부(히로와 면접술사)했다.

SKCT 실행역량에서 중요한 점은

1. 역할에 맞게 행동한다.
2. 주도적으로 당사자와 되도록 해결한다.
3. 업무상 문제라면 보고해서 조치를 받는다.

위 방식대로 풀면 어느정도 감이 잡힌다.

실행역량은 겨우 다풀었는데 뒤에 인적성은 10문제 넘게 못푼것 같다.

결과가 나와봐야 알 것 같다.

## < 코딩 테스트 >
진행 방식 : 3문제 / 2시간

코딩테스트 보는 기업중 복붙을 못하게하는 기업이 있는데, 정말 이해할수가 없다.

자바같은경우 오버라이드하면 다적어야하는데..

어느 줄에서 오류가 발생한지 알아내는 디버깅마저 힘들었다.

1번 문제는 단순 구현문제였는데 별로 어렵지 않았다.

2번 문제는 [괄호의 값](https://www.acmicpc.net/problem/2504) 문제와 유사하다.

입력으로 압축된 문자를 주어주고 이를 풀어내는 문제였다. ex) 4(3(h)g) => hhghhghhghhg

여기서 계속 인덱스오류가 나서 못찾고 결국 제출했다.

다시 생각해보니 Stack으로 풀면되는 문제였는데.. 아쉬움이 남는다.

3번 문제도 네트워크 연결 문제와 상당히 비슷했다.

시험 당시에는 Union-Find가 아닌 Dijkstra로 풀었는데, 어디서 꼬였는지 절반만 맞고, 시간초과가 난 상태로 제출했다.

한마디로 절반만 맞은셈이니까 틀린문제다.

여러 유형을 더 접해봐야 할것 같다.

2번문제를 Stack으로 해결한다고 좀더 빨리 생각해야했는데 하는 아쉬움이 남는다.

## < 결과 >

반응형
반응형

* 핵심 개념

Stack 자료구조를 할용해 풀 수 있는 문제다. 

1. 처음 Stack에 값을 넣는다. 

2. Stack이 비어있을 때까지 돌면서 자신보다 큰 탑을 찾는다. 

2 -1. 자신보다 큰 값을 찾으면 해당 탑의 idx를 출력 

2-2. 자신보다 큰 값이 없다면 0 출력 

3. Stack에 자신의 값 추가 

2~3 과정을 반복하면 풀 수 있는 문제다.

* 문제 

문제

KOI 통신연구소는 레이저를 이용한 새로운 비밀 통신 시스템 개발을 위한 실험을 하고 있다. 실험을 위하여 일직선 위에 N개의 높이가 서로 다른 탑을 수평 직선의 왼쪽부터 오른쪽 방향으로 차례로 세우고, 각 탑의 꼭대기에 레이저 송신기를 설치하였다. 모든 탑의 레이저 송신기는 레이저 신호를 지표면과 평행하게 수평 직선의 왼쪽 방향으로 발사하고, 탑의 기둥 모두에는 레이저 신호를 수신하는 장치가 설치되어 있다. 하나의 탑에서 발사된 레이저 신호는 가장 먼저 만나는 단 하나의 탑에서만 수신이 가능하다. 

예를 들어 높이가 6, 9, 5, 7, 4인 다섯 개의 탑이 수평 직선에 일렬로 서 있고, 모든 탑에서는 주어진 탑 순서의 반대 방향(왼쪽 방향)으로 동시에 레이저 신호를 발사한다고 하자. 그러면, 높이가 4인 다섯 번째 탑에서 발사한 레이저 신호는 높이가 7인 네 번째 탑이 수신을 하고, 높이가 7인 네 번째 탑의 신호는 높이가 9인 두 번째 탑이, 높이가 5인 세 번째 탑의 신호도 높이가 9인 두 번째 탑이 수신을 한다. 높이가 9인 두 번째 탑과 높이가 6인 첫 번째 탑이 보낸 레이저 신호는 어떤 탑에서도 수신을 하지 못한다.

탑들의 개수 N과 탑들의 높이가 주어질 때, 각각의 탑에서 발사한 레이저 신호를 어느 탑에서 수신하는지를 알아내는 프로그램을 작성하라. 

입력

첫째 줄에 탑의 수를 나타내는 정수 N이 주어진다. N은 1 이상 500,000 이하이다. 둘째 줄에는 N개의 탑들의 높이가 직선상에 놓인 순서대로 하나의 빈칸을 사이에 두고 주어진다. 탑들의 높이는 1 이상 100,000,000 이하의 정수이다.

출력

첫째 줄에 주어진 탑들의 순서대로 각각의 탑들에서 발사한 레이저 신호를 수신한 탑들의 번호를 하나의 빈칸을 사이에 두고 출력한다. 만약 레이저 신호를 수신하는 탑이 존재하지 않으면 0을 출력한다.

* 소스 코드

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
import java.io.*;
import java.util.*;
 
public class Main {
    public static StringTokenizer stk;
 
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        int n = Integer.parseInt(br.readLine());
        Stack<Pair> stack = new Stack<>();
        stk = new StringTokenizer(br.readLine());
        for (int i = 1; i <= n; i++) {
            int val = Integer.parseInt(stk.nextToken());
            if (stack.isEmpty()) {
                stack.push(new Pair(i, val));
                bw.write("0 ");
                continue;
            }
            while (!stack.isEmpty()) {
                if (stack.peek().val >= val) {  //stack.peek에 부딪히면
                    bw.write(stack.peek().idx + " ");
                    break;
                } else {
                    stack.pop();
                }
            }
            if (stack.isEmpty()) bw.write("0 ");
            stack.push(new Pair(i, val));
        }
        bw.flush();
        bw.close();
    }
 
    public static class Pair {
        int idx, val;
 
        public Pair(int idx, int val) {
            this.idx = idx;
            this.val = val;
        }
    }
}
cs


반응형
반응형

* 핵심 개념

시험 당시에는 17143번 낚시왕 문제보다 복잡해보여서 풀지 않았는데,

다시 풀어보니 역시 풀만한 문제였다. 

특별한 알고리즘 기법이 필요없이 단순히 새로운 크기의 맵을 하나 더 만들어서

새로운 맵에 시뮬레이션한 결과를 저장하고 이를 다시 원래맵에 돌려주기만 하면 된다.

* 문제

문제

미세먼지를 제거하기 위해 구사과는 공기청정기를 설치하려고 한다. 공기청정기의 성능을 테스트하기 위해 구사과는 집을 크기가 R×C인 격자판으로 나타냈고, 1×1 크기의 칸으로 나눴다. 구사과는 뛰어난 코딩 실력을 이용해 각 칸 (r, c)에 있는 미세먼지의 양을 실시간으로 모니터링하는 시스템을 개발했다. (r, c)는 r행 c열을 의미한다.

공기청정기는 항상 왼쪽 열에 설치되어 있고, 크기는 두 행을 차지한다. 공기청정기가 설치되어 있지 않은 칸에는 미세먼지가 있고, (r, c)에 있는 미세먼지의 양은 Ar,c이다.

1초 동안 아래 적힌 일이 순서대로 일어난다.

  1. 미세먼지가 확산된다. 확산은 미세먼지가 있는 모든 칸에서 동시에 일어난다.
    • (r, c)에 있는 미세먼지는 인접한 네 방향으로 확산된다.
    • 인접한 방향에 공기청정기가 있거나, 칸이 없으면 그 방향으로는 확산이 일어나지 않는다.
    • 확산되는 양은 Ar,c/5이고 소수점은 버린다.
    • (r, c)에 남은 미세먼지의 양은 Ar,c - (Ar,c/5)×(확산된 방향의 개수) 이다.
  2. 공기청정기가 작동한다.
    • 공기청정기에서는 바람이 나온다.
    • 위쪽 공기청정기의 바람은 반시계방향으로 순환하고, 아래쪽 공기청정기의 바람은 시계방향으로 순환한다.
    • 바람이 불면 미세먼지가 바람의 방향대로 모두 한 칸씩 이동한다.
    • 공기청정기에서 부는 바람은 미세먼지가 없는 바람이고, 공기청정기로 들어간 미세먼지는 모두 정화된다.

공기청정기의 바람은 다음과 같은 방향으로 순환한다.

방의 정보가 주어졌을 때, T초가 지난 후 구사과의 방에 남아있는 미세먼지의 양을 구해보자.

입력

첫째 줄에 R, C, T (6 ≤ R, C ≤ 50, 1 ≤ T ≤ 1,000) 가 주어진다.

둘째 줄부터 R개의 줄에 Ar,c (-1 ≤ Ar,c ≤ 1,000)가 주어진다. 공기청정기가 설치된 곳은 Ar,c가 -1이고, 나머지 값은 미세먼지의 양이다. -1은 2번 위아래로 붙어져 있고, 가장 윗 행, 아랫 행과 두 칸이상 떨어져 있다.

출력

첫째 줄에 T초가 지난 후 구사과 방에 남아있는 미세먼지의 양을 출력한다.

* 소스 코드

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
135
136
137
138
import java.io.*;
import java.util.*;
 
public class Main {
    public static StringTokenizer stk;
    public static Cleaner[] cleaner_pos = new Cleaner[2];   //공기청정기 위치 저장
    public static int r, c, t;
    public static int[][] map;
    public static int[] dx = {1-100};
    public static int[] dy = {001-1};
 
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        stk = new StringTokenizer(br.readLine());
        r = Integer.parseInt(stk.nextToken());
        c = Integer.parseInt(stk.nextToken());
        t = Integer.parseInt(stk.nextToken());
        map = new int[r][c];
        int idx = 0;
        for (int i = 0; i < r; i++) {
            stk = new StringTokenizer(br.readLine());
            for (int j = 0; j < c; j++) {
                map[i][j] = Integer.parseInt(stk.nextToken());
                if (map[i][j] == -1) {
                    cleaner_pos[idx++= new Cleaner(i, j);
                }
            }
        }
        for (int i = 0; i < t; i++) {
            spreadStart(new int[r][c]);
            cleanerStart(new int[r][c]);
        }
        getAns();
    }
 
    public static void spreadStart(int[][] nmap) {
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                nmap[i][j] += map[i][j];
                if (map[i][j] < 5continue;
                int spreadCnt = map[i][j] / 5;
                for (int k = 0; k < 4; k++) {
                    int ny = i + dy[k];
                    int nx = j + dx[k];
                    //해당 맵에 퍼트릴 수 있는지 확인
                    if (ny >= 0 && ny < r && nx >= 0 && nx < c && map[ny][nx] != -1) {
                        nmap[i][j] -= spreadCnt;
                        nmap[ny][nx] += spreadCnt;
                    }
                }
            }
        }
        spreadCopy(nmap);
    }
 
    public static void cleanerStart(int[][] nmap) {
        for (int idx = 0; idx < 2; idx++) {
            Cleaner curr = cleaner_pos[idx];
            int ny = curr.r;
            int nx = curr.c + 1;
            //오른쪽으로 끝까지
            while (nx < c - 1) {
                nmap[ny][nx + 1= map[ny][nx];
                nx++;
            }
            //상하로 끝까지
            if (idx == 0) {
                while (ny > 0) {
                    nmap[ny - 1][nx] = map[ny][nx];
                    ny--;
                }
            } else {
                while (ny < r - 1) {
                    nmap[ny + 1][nx] = map[ny][nx];
                    ny++;
                }
            }
            //좌측으로 끝까지
            while (nx > 0) {
                nmap[ny][nx - 1= map[ny][nx];
                nx--;
            }
            //공기청정기 위치 전까지
            if (idx == 0) {
                while (ny < curr.r - 1) {
                    nmap[ny + 1][nx] = map[ny][nx];
                    ny++;
                }
            } else {
                while (ny > curr.r + 1) {
                    nmap[ny - 1][nx] = map[ny][nx];
                    ny--;
                }
            }
        }
        cleanerCopy(nmap);
    }
 
    public static void spreadCopy(int[][] nmap) {
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                map[i][j] = nmap[i][j];
            }
        }
    }
 
    public static void cleanerCopy(int[][] nmap) {
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                if (i == 0 || i == r - 1 || j == 0 || j == c - 1 || i == cleaner_pos[0].r || i == cleaner_pos[1].r) {
                    map[i][j] = nmap[i][j];
                }
            }
        }
        map[cleaner_pos[0].r][cleaner_pos[0].c] = -1;
        map[cleaner_pos[1].r][cleaner_pos[1].c] = -1;
    }
 
    public static void getAns() {
        int ans = 0;
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                if (map[i][j] <= 0continue;
                ans += map[i][j];
            }
        }
        System.out.println(ans);
    }
 
    public static class Cleaner {
        int r, c;
 
        public Cleaner(int r, int c) {
            this.r = r;
            this.c = c;
        }
    }
}
cs


반응형
반응형

* 핵심 개념

처음에는 그냥 우선순위 큐를 두개썻는데 메모리 초과가 낫다.

해결하기 위해 최소 힙, 최대 힙으로 변경했다.

핵심 개념은 최대 힙의 peek는 최소 힙의 peek보다 작거나 같아야 한다.

* 문제

문제

수빈이는 동생에게 "가운데를 말해요" 게임을 가르쳐주고 있다. 수빈이가 정수를 하나씩 외칠때마다 동생은 지금까지 수빈이가 말한 수 중에서 중간값을 말해야 한다. 만약, 그동안 수빈이가 외친 수의 개수가 짝수개라면 중간에 있는 두 수 중에서 작은 수를 말해야 한다.

예를 들어 수빈이가 동생에게 1, 5, 2, 10, -99, 7, 5를 순서대로 외쳤다고 하면, 동생은 1, 1, 2, 2, 2, 2, 5를 차례대로 말해야 한다. 수빈이가 외치는 수가 주어졌을 때, 동생이 말해야 하는 수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에는 수빈이가 외치는 정수의 개수 N이 주어진다. N은 1보다 크거나 같고, 100,000보다 작거나 같은 자연수이다. 그 다음 N줄에 걸쳐서 수빈이가 외치는 정수가 차례대로 주어진다. 정수는 -10,000보다 크거나 같고, 10,000보다 작거나 같다.

출력

한 줄에 하나씩 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
40
41
42
43
44
45
46
47
import java.io.*;
import java.util.*;
 
public class Main {
    public static StringTokenizer stk;
    public static StringBuilder sb = new StringBuilder();
    public static PriorityQueue<Integer> minHeap;
    public static PriorityQueue<Integer> maxHeap;
 
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        int[] num = new int[n];
        minHeap = new PriorityQueue<>(Comparator.naturalOrder());
        maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
        for (int i = 0; i < n; i++) {
            num[i] = Integer.parseInt(br.readLine());
            if (i == 0) {
                maxHeap.add(num[i]);
                sb.append(num[i] + "\n");
                continue;
            }
            if (i % 2 == 0) {
                maxHeap.add(num[i]);
            } else {
                minHeap.add(num[i]);
            }
            if (changeCheck()) {
                swap();
            }
            sb.append(maxHeap.peek() + "\n");
        }
        System.out.println(sb);
    }
 
    public static boolean changeCheck() {
        if (minHeap.isEmpty() || maxHeap.isEmpty()) return false;
        return minHeap.peek() <= maxHeap.peek();
    }
 
    public static void swap() {
        int minTomax = minHeap.poll();
        int maxTomin = maxHeap.poll();
        minHeap.add(maxTomin);
        maxHeap.add(minTomax);
    }
}
cs


반응형
반응형

* Review

2019 상반기 공채 오전반 복원문제중 2번문제였다.

기본 삼성유형으로 알려진 BFS, DFS에서 그냥 구현문제가 나올줄은 몰랏지만

쉬웠다는 평이 많은 상반기 코테에서 1솔로 가능할지 의문이다.

* 핵심 개념

이 문제에서는 주의할 점이 두가지 있다.

1. 물고기들 각각을 움직일 때 바로 움직이지 않고, 한번에 움직여야 한다.

필자는 Fish 배열을 만들어 정보를 저장하고 상어 움직임이 끝낫을 때 한번에 이동했다.

물론, 조건에 따라 해당 좌표에 자신보다 작은 상어가 있다면 Fish 배열에 null로 바꿔주면 된다.

2. 시간 속도를 줄이려면 움직여야 하는 거리 % (Width or Depth * 2 - 2) 계산을 하고 움직이면 된다.(584ms -> 308ms)

아쉽게도 시험장에서 30분을 고민하다 이 공식을 못찾았다.

이 두가지 조건만 만족하면 어렵지 않게 풀수있는 기출문제다.

* 문제

문제

낚시왕이 상어 낚시를 하는 곳은 크기가 R×C인 격자판으로 나타낼 수 있다. 격자판의 각 칸은 (r, c)로 나타낼 수 있다. r은 행, c는 열이고, (R, C)는 아래 그림에서 가장 오른쪽 아래에 있는 칸이다. 칸에는 상어가 최대 한 마리 들어있을 수 있다. 상어는 크기와 속도를 가지고 있다.

낚시왕은 가장 처음에 1번 열의 한 칸 왼쪽에 있다. 다음은 1초 동안 일어나는 일이며, 아래 적힌 순서대로 일어난다. 낚시왕은 가장 오른쪽 열의 오른쪽 칸에 이동하면 이동을 멈춘다.

  1. 낚시왕이 오른쪽으로 한 칸 이동한다.
  2. 낚시왕이 있는 열에 있는 상어 중에서 가장 땅과 가까운 상어를 잡는다. 상어를 잡으면 격자판에서 잡은 상어가 사라진다.
  3. 상어가 이동한다.

상어는 입력으로 주어진 속도로 이동하고, 속도의 단위는 칸/초이다. 상어가 이동하려고 하는 칸이 격자판의 경계인 경우에는 방향을 반대로 바꿔서 속력을 유지한채로 이동한다.

왼쪽 그림의 상태에서 1초가 지나면 오른쪽 상태가 된다. 상어가 보고 있는 방향이 속도의 방향, 왼쪽 아래에 적힌 정수는 속력이다. 오른쪽 위에 상어를 구분하기 위해 문자를 적어 놓았다.

상어가 이동을 마친 후에 한 칸에 상어가 두 마리 이상 있을 수 있다. 이때는 크기가 가장 큰 상어가 나머지 상어를 모두 잡아먹는다.

낚시왕이 상어 낚시를 하는 격자판의 상태가 주어졌을 때, 낚시왕이 잡은 상어 크기의 합을 구해보자.

입력

첫째 줄에 격자판의 크기 R, C와 상어의 수 M이 주어진다. (2 ≤ R, C ≤ 100, 0 ≤ M ≤ R×C)

둘째 줄부터 M개의 줄에 상어의 정보가 주어진다. 상어의 정보는 다섯 정수 r, c, s, d, z (1 ≤ r ≤ R, 1 ≤ c ≤ C, 0 ≤ s ≤ 1000, 1 ≤ d ≤ 4, 1 ≤ z ≤ 10000) 로 이루어져 있다. (r, c)는 상어의 위치, s는 속력, d는 이동 방향, z는 크기이다. d가 1인 경우는 위, 2인 경우는 아래, 3인 경우는 오른쪽, 4인 경우는 왼쪽을 의미한다.

두 상어가 같은 크기를 갖는 경우는 없고, 하나의 칸에 둘 이상의 상어가 있는 경우는 없다.

출력

낚시왕이 잡은 상어 크기의 합을 출력한다.

* 소스 코드

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
import java.io.*;
import java.util.*;
 
public class Main {
    public static StringTokenizer stk;
    public static StringBuilder sb = new StringBuilder();
    public static int[] dy = {-1100};
    public static int[] dx = {001-1};
    public static int width, depth, n, ans = 0;
    public static Fish[] fishInfo = new Fish[10001];
    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());
        depth = Integer.parseInt(stk.nextToken());
        width = Integer.parseInt(stk.nextToken());
        n = Integer.parseInt(stk.nextToken());
        map = new int[depth + 1][width + 1];
        for (int i = 1; i <= n; i++) {
            stk = new StringTokenizer(br.readLine());
            int r = Integer.parseInt(stk.nextToken());
            int c = Integer.parseInt(stk.nextToken());
            int s = Integer.parseInt(stk.nextToken());
            int d = Integer.parseInt(stk.nextToken());
            int z = Integer.parseInt(stk.nextToken());
            fishInfo[z] = new Fish(z, c, r, d - 1, s);
            map[r][c] = z;
        }
        for (int i = 1; i <= width; i++) {
            //작살로 물고기 잡음
            for (int j = 1; j <= depth; j++) {
                if (map[j][i] != 0) {
                    fishInfo[map[j][i]] = null;
                    ans += map[j][i];
                    map[j][i] = 0;
                    break;
                }
            }
            //물고기 이동
            for (int j = 1; j <= 10000; j++) {
                if (fishInfo[j] == nullcontinue;
                Fish curr = fishInfo[j];
                int nx = curr.x;
                int ny = curr.y;
                int move = curr.speed;
                int d = curr.d;
                map[ny][nx] = 0;
                switch (d) {    //방향따라 case 다르게
                    //상하
                    case 0:
                    case 1:
                        move %= (depth * 2 - 2);
                        while (move > 0) {
                            if (ny == 1) {
                                d = 1;
                            }
                            if (ny == depth) {
                                d = 0;
                            }
                            ny += dy[d];
                            move--;
                        }
                        fishInfo[curr.idx] = new Fish(curr.idx, nx, ny, d, curr.speed);
                        break;
                    //좌우
                    case 2:
                    case 3:
                        move %= (width * 2 - 2);
                        while (move > 0) {
                            if (nx == 1) d = 2;
                            if (nx == width) d = 3;
                            nx += dx[d];
                            move--;
                        }
                        fishInfo[curr.idx] = new Fish(curr.idx, nx, ny, d, curr.speed);
                        break;
                }
            }
            for (int j = 1; j <= 10000; j++) {
                if (fishInfo[j] == nullcontinue;
                Fish curr = fishInfo[j];
                if (map[curr.y][curr.x] < curr.idx) {   //맵에 저장된 물고기보다 본인이 더 크면
                    fishInfo[map[curr.y][curr.x]] = null;
                    map[curr.y][curr.x] = curr.idx;
                }
            }
        }
        System.out.println(ans);
    }
 
    public static class Fish {
        int idx, x, y, d, speed;
 
        public Fish(int idx, int x, int y, int d, int speed) {
            this.idx = idx;
            this.x = x;
            this.y = y;
            this.d = d;
            this.speed = speed;
        }
    }
}
cs


반응형
반응형

* 핵심 개념

삼성 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


반응형
반응형

* 프로세스 동기화 배경

프로세스가 병행 또는 병렬로 실행될 때 여러 프로세스가 공유하는 데이터의 무결성이 유지되어야 한다.

병행 프로세스는 다중 처리 시스템이나 분산 처리 시스템에서 중요 개념으로 사용


* 임계 구역

한 프로세스가 자신의 임계구역에서 수행하는 동안에는 다른 프로세스들은 그들의 임계구역에 들어갈 수 없다

임계구역 안에서는 공유 변수를 변경, 테이블 갱신, 파일 쓰기 등의 작업을 수행한다.

임계구역에는 하나의 프로세스만 접근할 수 있으며, 해당 프로세스가 자원을 반납해야 다른 프로세스가 자원 및 데이터를 사용할 수 있다

* 임계구역 문제 해결 방법

1. 상호 배제 기법

특정 프로세스가 공유 자원을 사용하고 있을 경우 다른 프로세스가 해당 공유 자원을 사용하지 못하게 제어하는 기법

두 개의 프로세스 기준 : 피터슨 알고리즘(두 프로세스가 두 개의 데이터 항목을 공유하여 자원을 사용하는 방법)

여러 개의 프로세스 기준 : Lamport의 빵집 알고리즘(각 프로세스에 번호를 부여하여 자원을 사용하도록 하는 방볍)

2. 진행

자신의 임계구역에서 실행되는 프로세스가 없고 자신의 임계구역으로 진입하려는 프로세스가 있다면, 

잔류 구역에서 실행 중이지 않은 프로세스들만 임계영역에 진입 대상이 되고, 이 선택은 무한정 연기되지 않음!

3. 한계 대기

프로세스가 자기의 임계구역에 진입하려는 요청을 한 후로부터 요청이 허용될 때까지 다른 프로세스들이 임계영역에 그들 자신의 임계구역에 진입을 허용하는 횟수의 한계가 필요


* Mutex Lock

>> Mutex가 지켜주는 덕에 화장실을 한명만 사용할 수 있다.

즉, Mutex는 임계구역을 보호하고 프로세스간 경쟁을 방지하기 위해 사용

boolean available 변수를 사용 => 락의 가용 여부 표시

1
2
3
4
5
6
do {
    //Lock 획득
    임계구역
    //Lock 반환
    나머지 구역
while(true);
cs


* 세마포어(Semaphore)

Mutex와 유사하게 동작하지만 프로세스들이 자신들의 행동을 더 정교하게 동기화 할 수 있는 방법 제공

각 프로세스에 제어신호를 전달하여 순차적으로 진행하기 위한 동기화 구현

세마포 S = 정수 변수, wait(s)와 signal(s)로만 접근이 가능하다. S = 0인 경우 모든 자원이 사용 중을 의미

1
2
3
4
5
6
//wait(S)
//임계구역에 진입하기 전에 확인
if (S <= 0//누군가 사용하고 있다면
    S가 양수가 될 때까지 현재 프로세스를 list에서 기다림
else    //임계 구역에 진입이 가능하다면
    S = S - 1;
cs
=> 상호 배제 보장

1
2
3
4
5
6
//Signal(S)
//임계구역을 떠날 때 실행
if (자원 반환전에 list에서 기다리는 프로세스가 있다면)
    그 중 한개 프로세스를 임계 구역 진입
else     //list에서 기다리는 프로세스가 없다면
    S = S + 1;
cs

=> 진행 보장


* 고전적인 동기화 문제

1. 생산자-소비자 문제

생산자가 데이터를 생산하여 입력된 경우에만 접근하도록 구성하는 방식

생산자는 꽉찬 버퍼를 생산하고 소비자는 비어있는 버퍼를 생산한다.

2. 읽기-쓰기 문제

두 Reader가 동시에 공유 데이터를 접근하는 것은 허용하나, 동시에 Writer를 허용하지 않는 방식

Writer보다 Reader 요청이 많은 경우 병행성을 높일 수 있다.

3. 식사하는 철학자 문제

5명의 철학자들은 생각하고 먹는 일을 수행(철학자 : 프로세스, 젓가락 : 자원)

  1. 철학자는 양쪽의 젓가락을 사용하여 식사를 하고, 식사를 마친 철학자는 젓가락을 내려놓고 다시 생각한다.
  2. 철학자는 2개의 젓가락을 손에 쥐어야 식사를 할 수 있고, 한 번에 하나씩 잡을 수 있다.
  3. 옆 사람이 식사중이면 젓가락 잡는 것을 대기한다.

5명의 철학자가 동시에 식사를 하게 되면 모두 왼쪽 포크만을 가지게 되고 오른쪽 포크를 대기하는 교착 상태(Deadlock)이 발생한다.

이를 방자힉 위한 방법은 대표적으로 3가지이다.

  1. 최대 4명의 철학자들만이 테이블에 앉을 수 있도록 한다. => 젓가락이 하나 남기 때문에 어떤 철학자는 식사가 가능하다.
  2. 한 철학자가 동시에 두개의 젓가락을 모두 집을 수 있을 때만 젓가락을 집도록 허용한다.(즉, 임계구역 안에서만 젓가락을 집어야 한다)
  3. 비대칭 해결안을 사용한다.(홀수 번 철학자는 왼쪽, 짝수 번 철학자는 오른쪽 젓가락부터 집는다)


반응형