알고리즘 연습이 소홀해져서 오랜만에 백준을 풀다가 특이한 제목의 문제라서 한 번 시도했다가 꽤 오래 풀었다. 문제 자체는 쉬운데 시간제한을 맞춰줘야 하기 때문에 정렬과 탐색을 적절히 써야하는 문제다.
과도한 컨셉의 폐혜
입출력 예시
정답코드
#include <stdio.h> #include <stdlib.h> #include <string.h> struct pocketmon { char name[21]; int i; }; int isInteger(char *); int pocketmonSearch(struct pocketmon *, char *, int, int); void quickSort(struct pocketmon *, int, int); int inPlacePartition(struct pocketmon *, int, int, int); int main() { int n, m; struct pocketmon *pocketmon; struct pocketmon *tmp; int i; char search[21]; scanf("%d %d", &n, &m); pocketmon = (struct pocketmon *)malloc(sizeof(struct pocketmon) * n); tmp = (struct pocketmon *)malloc(sizeof(struct pocketmon) * n); for (i = 0; i < n; i++) { scanf("%s", pocketmon[i].name); pocketmon[i].i = (i + 1); strcpy(tmp[i].name, pocketmon[i].name); tmp[i].i = (i + 1); } quickSort(pocketmon, 0, n - 1); for (i = 0; i < m; i++) { scanf("%s", search); if (isInteger(search)) { // 입력이 숫자일 시 printf("%s\n", tmp[atoi(search) - 1].name); } else { // 입력이 이름일 시 printf("%d\n", pocketmonSearch(pocketmon, search, 0, n - 1)); } } return 0; } int isInteger(char *str) { int i; for (i = 0; str[i]; i++) { if (!('0' <= str[i] && str[i] <= '9')) return 0; else return 1; } } int pocketmonSearch(struct pocketmon *pocketmon, char *search, int l, int r) { int m = (l + r) / 2; if (strcmp(pocketmon[m].name, search) == 0) return pocketmon[m].i; else if (strcmp(pocketmon[m].name, search) < 0) return pocketmonSearch(pocketmon, search, m + 1, r); else return pocketmonSearch(pocketmon, search, l, m - 1); return 0; } void quickSort(struct pocketmon *po, int l, int r) { int a, b; if (l >= r) return; a = b = inPlacePartition(po, l, r, r); quickSort(po, l, a - 1); quickSort(po, b + 1, r); } int inPlacePartition(struct pocketmon *po, int l, int r, int k) { struct pocketmon p; struct pocketmon tmp; int i, j; p = po[k]; tmp = po[k]; po[k] = po[r]; po[r] = tmp; i = l; j = r - 1; while (i <= j) { while (i <= j && (strcmp(po[i].name, p.name) <= 0)) { i++; } while (j >= i && (strcmp(po[j].name, p.name) >= 0)) { j--; } if (i < j) { tmp = po[i]; po[i] = po[j]; po[j] = tmp; } } tmp = po[i]; po[i] = po[r]; po[r] = tmp; return i; }
코드가 너무 길어! 아악 내눈!
코드설명
1. 포켓몬 구조체를 만든다. 포켓몬의 이름과 포켓몬이 입력된 순서가 멤버변수로 존재한다.
2. n (입력받을 포켓몬의 수), m(검색할 포켓몬의 수), pocketmon (이름으로 검색 시 사용할 포켓몬 배열), tmp (숫자로 검색 시 사용할 포켓몬 배열)을 선언한다. n과 m을 받고 pocketmon과 tmp를 동적할당 한다.
* 포켓몬 구조체 배열을 두개로 나눈 이유는 검색시간을 최소화 하기 위해서이다. 문자열 배열 하나로 해결했을때는 금방 했는데, 시간초과가 떴다! 아마 테스트케이스에 검색 시간이 최악의 경우로 걸리는 경우가 있는 것 같다.
* 검색을 n과 같은 횟수로 하고 항상 n번째에 있는 포켓몬 이름만 검색하면 무려 O(n^2)이다.
* 포켓몬 이름 검색의 시간복잡도를 최소화 하기 위해 (제자리)퀵 정렬을 사용했고, 퀵 정렬을 하면 인덱스 번호가 흐트러지기 때문에 구조체를 사용해 입력 당시 순서를 저장했다. 퀵 정렬의 기준원소는 항상 r번째 (분할 된 인덱스 중 가장 오른쪽)으로 했다.
* 퀵 정렬 사용을 위해 구조체를 정렬하면, 숫자로 검색 했을 때 또 위와 같이 O(n^2)의 경우에 걸릴 수 있기 때문에, 입력 당시 순서와 똑같이 보존할 배열을 하나 더 만들었다. (tmp)
3. 포켓몬의 이름을 입력받는다. pocketmon과 tmp에 이름과 입력받을 당시 순서를 저장한다. (tmp에 순서저장은 생략해도 된다.)
4. pocketmon 배열을 퀵 정렬한다.
5. m번 만큼 검색할 포켓몬의 이름 혹은 순서를 입력받는다. 순서(숫자)를 입력받았을 땐 tmp를 이용해 O(1)의 시간만에 검색하고, 이름을 입력받았을 땐 이진탐색을 이용해 O(log n)의 시간만에 검색한다.
6. 최종적으로, 퀵 정렬에 O(n * log n)의 시간이 걸리니 총 프로그램의 시간복잡도는 O(n * log n)이다. 시간 제한을 통과할 수 있다.
문제 풀기 참 힘들당.
더 짧고 효율적으로 풀 방법을 생각해야겠다.
'BOJ' 카테고리의 다른 글
[C] 백준 3273번: 두 수의 합 (0) | 2018.10.28 |
---|---|
[C] 백준 13458번: 시험 감독 (0) | 2018.08.12 |
[C] 백준 1205번: 등수 구하기 (0) | 2018.07.31 |