2019.04.06 08:00 a.m. ~ 2019.04.07 11:00 a.m. 까지 총 27시간 동안 구글 코드잼 qualification이 치뤄졌습니다.

코드잼은 유명한 대회라서 많이 들어보셨을 텐데, 저는 처음 참가해봤어요.

저번에 본 킥스타트랑 같은 플랫폼에서 진행되서 그런지 낯설지는 않았습니다. ㅎㅎ

총 네 문제가 나오는 데, 저는 첫번째, 두번째 문제를 맞췄습니다. 세번째 문제는 테스트 케이스는 만족하는 데 플랫폼에서 돌려보면 런타임에러가 나요ㅠㅠ

 

Qualification은 30점만 넘으면 다음 라운드로 진출할 수 있다고 합니다. 다행이네요ㅎㅎ

심기일전해서 다음 라운드에서는 더 좋은 결과 내겠습니다.

새로운 해결 방향 : 사무실과의 거리가 k보다 더 큰 지역들을 대상으로 모든 거리가 k 이하인 지점에 새로운 사무실을 건설하는 방향입니다. 

만약, 이 k값이 주어진다면 만족하는 어떤 '0' 지점들과 사무실과의 거리가 k이상인 지점들과의 거리를 구해 모든 거리가 k-1이하면 그 지점이 '1'의 후보지가 되는 것이지요. 이것을 sub-problem이라 하겠습니다.

먼저, 주어진 map상태에서 bfs를 실행해 '0' 지점들의 사무실과의 최소 거리를 구합니다.

구해진 거리 정보는 큐에 쌓이게 되는데요, 나중에 '1'의 후보지를 탐색하기 위해 거리가 처음 큐에 저장되는 인덱스 정보를 따로 배열에 저장합니다. 

 

예제) 큐에 0 0 0 1 1 1 2 2 2 3 ... 이런 식으로 거리 정보가 쌓인 다면, 배열에 0,3,6,9..... 이런식으로 정보가 저장됩니다.

 

그 후, 어떤 거리 k에 대해서 sub-problem에 대한 답을 찾습니다. 

k의 범위는 1부터 가장 멀리 떨어진 거리가 되겠습니다. 

모든 범위를 탐색해야하기 때문에 '이진탐색'을 적용하면 효율을 높일 수 있겠죠?!

 

예제)

1 0(1) 0(2)
0(1) 0(2) 0(3)
0(2) 0(3) 0(4)
빨간 숫자는 사무실과의 거리를 나타냅니다.

이진탐색을 적용하기 떄문에 시작(1)+끝(4) / 2 값이 2부터 탐색합니다.

거리가 3 이상인 지점은 3개 있죠. 그 지점들과 '0'인 지점들의 거리를 구합니다.

모든 거리가 2 이하인 지점이 있다면 그 지점이 '1'의 후보지가 되는 것이고 '2'가 정답이 될 수 있는 것입니다.

그 후, 조건을 만족하는 지점이 있다면 k 왼쪽 부분 (시작(1)+k-1(1)) 을 탐색하고, 

없다면 k의 오른쪽 부분을(k+1(3),4) 탐색하면 됩니다.

여기선 조건을 만족하기 (2,2)지점이 조건을 만족하기 때문에 일단 정답은 2로 설정한 후왼쪽 부분을 탐색합니다.

k = 1일 때, 탐색해보시면 안되는 것을 알 수 있기 때문에 정답은 2가 되는 것이지요.

 

이런 식으로 방향을 잡고, 코드를 짜니 이전 글에서 소개한 완전탐색의 문제를 해결할 수 있었습니다.

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
139
140
141
142
143
144
145
146
147
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
 
using namespace std;
 
int r, c;
 
int map[250][250]; // map 기본 정보
int distance_list[500]; // 거리정보 
int max_distance = 0// 가장 긴 거리
 
int check[250][250];
 
int dir_x[4= { 1,0,-1,0 };
int dir_y[4= { 0,1,0,-1 };
 
int one_x[62500]; // 배달 시간 구하는 que
int one_y[62500];
int one_d[62500];
int one_top = 0;
int one_rear = 0;
 
int zero_x[62500];
int zero_y[62500];
int zero_num = 0;
 
int k = 0;
 
void bfs() {
    while (one_rear < one_top) {
        int x = one_x[one_rear];
        int y = one_y[one_rear];
        int d = one_d[one_rear++];
 
        for (int i = 0; i < 4; i++) {
            int next_x = x + dir_x[i];
            int next_y = y + dir_y[i];
            int next_d = d + 1;
 
            if (next_x >= 0 && next_y >= 0 && next_x < c && next_y < r) {
                if (check[next_y][next_x] == 0 && map[next_y][next_x] == 0) {
                    check[next_y][next_x] = 1;
 
                    one_x[one_top] = next_x;
                    one_y[one_top] = next_y;
                    if (distance_list[next_d] == 0// 거리의 시작 인덱스를 저장
                        distance_list[next_d] = one_top;
                    one_d[one_top++= next_d;                    
                }
            }
        }
    }
}
 
void binary_search(int start, int end) {// 이진탐색
    if (start <= end) {
        int num = (start + end/ 2;
        int bigger = 0;
        int smaller = 0;
 
        for (int i = 0; i < zero_num; i++) { // 0지점들과 거리 비교
            for (int j = distance_list[num + 1]; j < one_top; j++) {
                int d = abs(zero_x[i] - one_x[j]) + abs(zero_y[i] - one_y[j]);
 
                if (d > num) {
                    bigger = 1;
                    break;
                }
            }
            if (bigger == 0) {
                smaller = 1;
                break;
            }
            bigger = 0;
        }
 
        if (smaller == 1) { // 왼쪽 부분 탐색
            k = num;
            binary_search(start, num - 1);
        }
        else { //오른쪽 부분 탐색
            binary_search(num + 1end);
        }
    }
}
 
int main() {
    int T;
    int idx = 0;
 
    cin >> T;
 
    while (idx++ < T) {
        string s;
 
        cin >> r >> c;
 
        for (int i = 0; i < r; i++) {
            cin >> s;
            for (int j = 0; j < c; j++) {
                map[i][j] = s[j] - '0';
                if (map[i][j] == 1) {
                    one_x[one_top] = j;
                    one_y[one_top] = i;
                    one_d[one_top++= 0;
                }
                else if(map[i][j] == 0) { // 나중에 거리비교를 위해 0인 지점의 정보들을 따로 저장
                    zero_x[zero_num] = j;
                    zero_y[zero_num++= i;
                }
            }
        }
        if (zero_num <= 1// 사무실을 하나 새로 건설하기 때문에 0이 한개 이하면 총 배달 시간은 0이 된다.
            cout << "Case  #" << idx << ": 0" << endl;
        else if (one_top == 0) { // 1이 하나도 없는 경우에는 가장 중간에 사무실을 건설
            map[r / 2][c / 2= 1;
            one_x[one_top] = r/2;
            one_y[one_top] = c/2;
            one_d[one_top++= 0;
 
            bfs();
 
            cout << "Case #" << idx << ": " << one_d[one_top - 1<< endl;
        }
        else {
            bfs();
 
            max_distance = one_d[one_top - 1];
            binary_search(1, max_distance);
 
            if(k != 0)
                cout << "Case #" << idx << ": " << k << endl;
            else
                cout << "Case #" << idx << ": " << max_distance << endl;
        }
 
        k = 0;
        one_top = 0;
        one_rear = 0;
        zero_num = 0;
        memset(check, 0sizeof(check));
        memset(distance_list, 0sizeof(distance_list));
    }
}
 
 

 

기존 bfs문제를 풀 때는 bfs만 적용하던가 완전탐색을 적용했을 때, 풀리는 경우가 대부분이었는데 역시 세계적인 대회이다 보니 문제의 난이도가 높은 것 같아요.

 

그래도 새로운 문제해결 방법을 알게되어서 기분이 좋습니다.

 

앞으로 더 열심히 해야겠습니다.ㅎㅎㅎ

 

다음 문제도 얼른 해결해서 포스팅하겠습니다. 감사합니다~

 

의견이 있으시거나 수정해야할 부분이 있으면 편하게 공유 부탁드립니다!

 

이 글은 한국 시간으로 2019년 3월 24일 오후 1시에 치뤄진  Google Kick Start 2019 Round A에 나온 문제에 대해서 다룹니다.

 

problem B : Parcles


Q. 격자로 표현된 지도(r X c)에 파르셀을 배달하는 지점들이 있다. 이 지도에 지점을 하나 더 세워서 고객들에게 파르셀을 배송할 때, 모든 고객에게 배송될 수 있는 최소시간을 구하시오. 단, 지도는 입력으로 주어지며, 지점은 1, 고객은 0으로 표현되고 새로운 지점은 0에 세울 수 있다. 배달 시간은 가장 가까운 지점과의 거리( |r1-r2| + |c1-c2|)로 정의된다. 

처음, 이 문제를 접근 할 때, bfs에 완전탐색을 접목하는 방향으로 문제를 해결하려 했습니다. 모든 0을 1로 치환한 다음 bfs를 적용한 다음, 원하는 답을 찾는 것이지요. 이 접근방법에 대한 코드입니다.

 
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
#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
 
using namespace std;
 
int map[250][250];
int check[250][250];
int r, c;
int minimum = 500;
int checking = 0;
 
int zero_x[62500];
int zero_y[62500];
int zero_top = 0;
 
int one_x[62500];
int one_y[62500];
int d1[62500];
int one_top = 0;
int one_rear = 0;
 
int dir_x[4= { 1,0,-1,0 };
int dir_y[4= { 0,1,0,-1 };
 
void bfs() {
 
    while (one_rear < one_top) {
        int x = one_x[one_rear];
        int y = one_y[one_rear];
        int d = d1[one_rear++];
 
        for (int i = 0; i < 4; i++) {
            int next_x = x + dir_x[i];
            int next_y = y + dir_y[i];
            int next_d = d + 1;
 
            if (next_x >= 0 && next_x < c && next_y >= 0 && next_y < r) {
                if (check[next_y][next_x] == 0 && map[next_y][next_x] == 0) {
                    check[next_y][next_x] = 1;
                    one_x[one_top] = next_x;
                    one_y[one_top] = next_y;
                    d1[one_top++= next_d;
 
                    if (next_d >= minimum) {
                        checking = 1;
                        return;
                    }
                }
            }
        }
    }
}
int main() {
    int T;
    int idx = 0;
 
    cin >> T;
 
    while (idx++ < T) {
        cin >> r >> c;
 
        for (int i = 0; i < r; i++) {
            string s;
 
            cin >> s;
 
            for (int j = 0; j < c; j++) {
                map[i][j] = s[j] - '0';
 
                if (map[i][j] == 0) {
                    zero_x[zero_top] = j;
                    zero_y[zero_top++= i;
                }
                if (map[i][j] == 1) {
                    one_x[one_top] = j;
                    one_y[one_top] = i;
                    d1[one_top++= 0;
                }
            }
        }
 
        if (zero_top <= 1) { // 새로운 office를 세우기 때문
            cout << "Case  #" << idx << ": " << "0" << endl;
        }
        else {
            int num = one_top;
 
            for (int i = 0; i < zero_top; i++) { // 완전탐색 모든 '0'이 '1'의 후보가 됨
                one_x[one_top] = zero_x[i];
                one_y[one_top] = zero_y[i];
                d1[one_top] = 0;
                map[one_y[one_top]][one_x[one_top++]] = 1;
 
                bfs();
 
                if (checking == 0)
                    minimum = min(minimum, d1[one_top - 1]);
 
                map[one_y[num]][one_x[num]] = 0;
                one_top = num;
                one_rear = 0;
                checking = 0;
                memset(check, 0sizeof(check));
            }
 
            cout << "Case #" << idx << ": " << minimum << endl;
        }
        minimum = 500;
        zero_top = 0;
        one_top = 0;
        one_rear = 0;
    }
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
 

 

이 코드는 visible 테스트 케이스를 통과하기에는 충분했지만, hidden 테스트 케이스는 time limit을 만족하지 못합니다. 

이 방향을 유지하며 time limit을 만족하기 위해 코드를 수정하고 또 수정했는데 실패했습니다.

Kick Start는 친절하게도 대회가 끝나고 나면 문제와 해결 방법을 오픈 합니다.

그래서 저는 해결방법을 참고해 코드를 수정했는데, 다음 글에서 소개하겠습니다.

 

이 글은 한국 시간으로 2019년 3월 24일 오후 1시에 치뤄진  Google Kick Start 2019 Round A에 나온 문제에 대해서 다룹니다.

 

 

problem A : Training


 

Q. 한 학교의 축구 코치로서 N명의 선수 중 대표 P명을 선발해야 한다. P명의 선수를 선발할 때, P명 모두 실력이 같아야한다. 한 시간에 한 명씩 트레이닝할 수 있으며 트레이닝 받은 선수는 실력이 1씩 오른다. 이 때, P명의 선수를 선발하는 데 걸리는 최소 시간을 구하시오. 단, 처음 선수들의 실력은 입력으로 주어진다.

 

저는 선수들의 실력을 배열에 저장해 정렬한 후 P번 선수부터 n번 선수까지 기준 실력으로 했을 때, 필요한 시간들을 비교해 최소 시간을 구하는 식으로 방향을 잡았습니다. 

정렬을 하는 이유는 기준 실력 아래의 선수 p-1명의 선수들이 그 기준 실력에 대해 최소시간이 요구되는 선수들이기 때문입니다. 

모든 선수들이 가져야할 실력은 (기준 실력 * P) 입니다. 

실력 1을 올리기 위해 1시간이 필요하므로 선수들이 기준 실력이 되기위해 필요한 시간은 (기준실력 - 현재실력) 입니다. 

x번째 선수가 기준실력인 팀이 되기 위해 필요한 최소 트레이닝 시간은 (기준실력 - x-p-1번째 선수 실력) + ..... +  (기준실력 - x-1번째 선수 실력) +  (기준실력 - x번째 선수 실력)이 되겠습니다.

위의 식을 정리하면, (기준 실력 * p) - (x-p-1번째 선수의 실력부터 x번째 선수의 실력의 합) 이라는 점화식이 나옵니다.

x번째 선수는 트레이닝할 시간이 필요없으므로 제외시킨다면 (기준 실력 * p - 1) - (x-p-1번째 선수의 실력부터 x -1 번째 선수의 실력의 합)이라고 할 수 있겠네요.

 

예제) n = 6, p = 4, 선수들 실력 = 5 5 1 2 3 4

 

(1) 실력들을 정렬 => 1 2 3 4 5 5

(2) 4번째 선수의 실력 부터 6번째 선수의 실력을 기준으로 설정.

 

1 2 3 4 5 5 => 4 * 3 - (1 + 2 + 3) = 6

1 2 3 4 5 5 => 5 * 3 - (2 + 3 + 4) = 6

1 2 3 4 5 5 => 5 * 3 - (3 + 4 + 5) = 3

 

n과 p가 커지면, (x-p-1번째 선수의 실력부터 x -1 번째 선수의 실력의 합)을 반복문을 통해 구할 때 시간을 많이 잡아먹습니다.

 

이를 방지하기 위해, 

1) 첫번째, 총합만 반복문을 통해 구합니다.

2) 기준 실력이 바뀔 때, 맨 첫번째 실력을 빼고 마지막 다음의 실력을 더하면서 기준이 바뀔때 마다 반복문을 사용하는 것을 방지합니다.

 

1 2 3 4 5 5 => 반복문을 통해 총합 6을 구함.

1 2 3 4 5 5 => 6에서 1을 빼고 4를 더함 => 9

1 2 3 4 5 5 => 9에서 2를 빼고 5를 더함 => 12

 

이런 과정에서 최소값을 구하면 정답을 찾을 수 있습니다!

 

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
#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
 
using namespace std;
 
int s[10000];
 
int main() {
    int T;
    int idx = 0;
 
    cin >> T;
 
    while (idx++ < T) {
        int n, p;
        int minimum = 1000000;
 
        cin >> n >> p;
 
        for (int i = 0; i < n; i++) {
            cin >> s[i];
        }
 
        sort(s, s + n);
 
        int sum = 0;
 
        for (int i = 0; i < p - 1; i++) { // 첫번째 총합
            sum += s[i];
        }
 
        minimum = s[p - 1* (p - 1- sum;
 
        for (int i = p; i < n; i++) { 
            sum = sum - s[i - p] + s[i - 1]; // 총합을 구하는 식
            int total = s[i] * (p - 1); // 팀을 꾸리기 위한 전체의 실력의 합
            minimum = min(minimum, total - sum); 
        }
 
        cout << "Case #" << idx << ": " <<minimum << endl;
    }
}
cs

 

 

처음으로, 문제풀이 글을 작성했는데, 처음인지라 많이 어색하고 너무 구구절절 설명하지 않았나 생각이 드네요(머쓱;;)

 

다른 의견이 있으시거나 수정해야할 부분이 있으면 편하게 공유 부탁드립니다!

 

안녕하세요! 코딩을 잘하고 싶은 승케이 입니다.



오늘 진행된 Kick Start에 대한 후기에 대해 써보려고 합니다.

우선 킥스타트에 대해 간단히 소개드리자면 구글에서 주관하는 대회로, 코드 잼과 비슷한 방식으로 진행되는 대회에요. 차이점이라면 다양한 시간에 예선이 이루어진다는 것입니다. 예선에서 좋은 성적을 거두면 구글에서 연락이 온다고도 합니다.


본격적으로 오늘 치룬 예선에 대해 말씀드리겠습니다. 


처음 시작할 때, 약 5분간 문제 리스트가 뜨지 않아 당황했는데, 시간이 조금 지나니 정상적으로 대회가 진행되었습니다.

저만의 문제가 아니었는지 마감 시간이 15분 연장되었다고 공지가 떴네요.


대회 진행 중, 공지사항이나 질문에 대한 답은 옆에 Questions 버튼을 누르면 볼 수 있어서 편했습니다.


문제는 총 3문제 였습니다. 첫번째, 두번째 문제는 나름 평이한 편이라고 생각해 세번째 문제를 제일 먼저 풀었습니다.

샘플에 대한 답은 나오는데 코드를 제출했을 때, 시간초과가 뜨네요ㅜㅜ 시간을 많이 투자했는데 아직 이런 제약조건을 만족시키기엔 내공이 많이 딸리는 것 같습니다.ㅜㅜ

그 이후에 첫번째, 두번째 문제를 해결하고 제출을 누르려는데 제출버튼이 작동이 안돼서 질문을 하니 원래 이런 이슈가 있다고 합니다.ㅎㅎ 해결은 안해줍니다. 당황스럽죠?

그렇게 두문제는 제출도 못하고 끝났네요. 아쉽습니다. 지난 문제들을 풀 수 있으니 그 때, 다시 제출해봐야겠어요.


대학교 1학년 떄, ACM-ICPC 예선 이후로 정말 오랜만에 국제 대회에 참여했는데요, 코딩테스트를 그래도 많이 봤다고 생각했는데, 역시 처음 접하는 플랫폼이라 그런가 시간배분도 잘 안되고 많이 헤맸네요. 


Kick Start 같은 경우에는 많이 유명한 대회는 아닌거 같아요. 그래서인지 진행부분이 미흡한 거 같기두 하구요. 아니면 너무 많은 사람이 참여해서 그런가요 ㅎㅎ 


아무튼 round A 이후에도 round H까지 아직 예선이 많이 남았으니 round A 놓치신 분들 다른 예선에는 참여하시길 바랍니다. 


친절하게도 한국 참가자를 위한 추천시간도 적어줬네요 ㅎㅎ. 


의견이 있으시거나 수정해야할 부분이 있으면 편하게 공유 부탁드립니다!


+ Recent posts