본문 바로가기
BOJ

[C] 백준 1620번: 나는야 포켓몬 마스터 이다솜

by 야호호코코 2018. 10. 26.
반응형

 알고리즘 연습이 소홀해져서 오랜만에 백준을 풀다가 특이한 제목의 문제라서 한 번 시도했다가 꽤 오래 풀었다. 문제 자체는 쉬운데 시간제한을 맞춰줘야 하기 때문에 정렬과 탐색을 적절히 써야하는 문제다.


과도한 컨셉의 폐혜


입출력 예시




정답코드

#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