controlpro

[알고리즘 문제해결 전략] 보글게임 본문

코딩/알고리즘 문제해결 전략

[알고리즘 문제해결 전략] 보글게임

controlpro 2021. 1. 1. 19:34
728x90

친구의 추천으로 <프로그래밍 대회에서 배우는 알고리즘 문제해결 전략> 이라는 책을 샀다. 책 내용을 전부다 정리 할 수는 없겠지만 이 책을 통해 많이 배우자! 


문제

보글(Boggle) 게임은 그림 (a)와 같은 5x5 크기의 알파벳 격자인
게임판의 한 글자에서 시작해서 펜을 움직이면서 만나는 글자를 그 순서대로 나열하여 만들어지는 영어 단어를 찾아내는 게임입니다. 펜은 상하좌우, 혹은 대각선으로 인접한 칸으로 이동할 수 있으며 글자를 건너뛸 수는 없습니다. 지나간 글자를 다시 지나가는 것은 가능하지만, 펜을 이동하지않고 같은 글자를 여러번 쓸 수는 없습니다.

예를 들어 그림의 (b), (c), (d)는 각각 (a)의 격자에서 PRETTY, GIRL, REPEAT을 찾아낸 결과를 보여줍니다.

보글 게임판과 알고 있는 단어들의 목록이 주어질 때, 보글 게임판에서 각 단어를 찾을 수 있는지 여부를 출력하는 프로그램을 작성하세요.

주의: 알고리즘 문제 해결 전략 6장을 읽고 이 문제를 푸시려는 분들은 주의하세요. 6장의 예제 코드는 이 문제를 풀기에는 너무 느립니다. 6장의 뒷부분과 8장을 참조하세요.

입력

입력의 첫 줄에는 테스트 케이스의 수 C(C <= 50)가 주어집니다. 각 테스트 케이스의 첫 줄에는 각 5줄에 5글자로 보글 게임판이 주어집니다. 게임판의 모든 칸은 알파벳 대문자입니다.
그 다음 줄에는 우리가 알고 있는 단어의 수 N(1 <= N <= 10)이 주어집니다. 그 후 N줄에는 한 줄에 하나씩 우리가 알고 있는 단어들이 주어집니다. 각 단어는 알파벳 대문자 1글자 이상 10글자 이하로 구성됩니다.

출력

각 테스트 케이스마다 N줄을 출력합니다. 각 줄에는 알고 있는 단어를 입력에 주어진 순서대로 출력하고, 해당 단어를 찾을 수 있을 경우 YES, 아닐 경우 NO를 출력합니다.  https://algospot.com/judge/problem/read/BOGGLE

 

algospot.com :: BOGGLE

보글 게임 문제 정보 문제 보글(Boggle) 게임은 그림 (a)와 같은 5x5 크기의 알파벳 격자인 게임판의 한 글자에서 시작해서 펜을 움직이면서 만나는 글자를 그 순서대로 나열하여 만들어지는 영어

algospot.com

 

문제는 다음과 같고, 나는 아직 6장을 하고 있으니까 저 algospot기준으로 하지 않고 다 돌아가기만 하도록 만들어 봤다. 

 

일단 이 문제를 해결하기 위해서는 재귀함수를 사용하라고 나와 있는데, 특정 조건이 계속해서 반복해서 나오면 for을 계속해서 사용하는 것보다, 재귀함수를 이용해서 푸는 것이 좋다고 한다. 

 

재귀함수를 사용하기 위해서는 기저 조건 즉 loop를 탈출하는 조건이 가장 중요하다.(이거 안해서 계속 false 났음 ㅠ)

 

1. 문제 분할 하기 

일단 모든 경우를 체크하면서 map을 탐색하기 때문에 입력한 string의 첫 글자가 시작되는 함수를 하나 만들어야하고 그 다음은 다음 idx의 글자가 주변 8칸에 있는지 확인을 해야한다 . 

 

여기서 다음  idx의 글자가 주변 8칸에 있는지 확인하는 과정이 계속해서 반복되니까 이 부분을 재귀함수로 구현하면 될거 같다. 

 

2. 기저조건 만들기 

  • (x ,y) 좌표가 문제에서 주어진 5 by 5를 넘어갔을 때 
  • 원하는 글자가 한 글자일 경우 바로 성공 
  • 글자를 다 찾은 경우

 

3. Coding 

코딩할때 c로 할까 C++로 할까 고민하다가 C++로 짜서 중간중간 C코드의 흠적이 보인다 .. ㅎ 이해좀 

#include <iostream>
#include <string>
#include <cstring>
using namespace std;
char map[5][5] = {0,};
char word[10];
int dy[8] = { -1,-1,-1,1,1,1,0,0 }; 
int dx[8] = { -1,0,1,-1,0,1,-1,0 };
int nextFind(int by, int bx, int idx , char *word) {
	// Rangecheck 기저함수를 잘체그하자
	int nextidx = idx + 1;
	if (by < 0 || by >= 5 || bx < 0 || bx >= 5) {
		return 0;
	}
	if (nextidx >= strlen(word)) { 
		return 1;
	}
	if (strlen(word) == 1)
		return 1;
	for (int direction = 0; direction < 8; direction++) {
		int nextY = by + dy[direction];
		int nextX = bx + dx[direction];
		if (map[nextY][nextX] == word[nextidx]) {
;
			if (nextFind(nextY, nextX, nextidx , word))
				return 1;
		}
	}
	return 0;
}
int startWord(char *word) {
	int bx, by;
	for (by = 0; by < 5; by++) {
		for (bx = 0; bx < 5; bx++) {
			if (map[by][bx] = word[0]) {
				if (nextFind(by, bx, 0 , word)) {
					return 1;
				}
			}
		}
	}
	return 0;
}
int main(void) {
	int testcase , tc;
	int i, j;
	int num;
	printf("input testcase : ");
	scanf_s("%d", &testcase);
	for (tc = 0; tc < testcase; tc++) {
		for (i = 0; i < 5; i++) {
			for (j = 0; j < 5; j++) {
				printf("input %d by %d : ", i+1, j+1);
				cin >> map[i][j];
			}
		}
		printf("input num :");
		scanf_s("%d", &num);
		for (i = 0; i < num; i++) {
			cin >> word;
			if (startWord(word))
				printf("%s : YES\n", word);
			else
				printf("%s : NO\n", word);
		}
	}
}

 

#include <iostream>

#include <string>

#include <cstring>

using namespace std;

char map[5][5] = {0,};

char word[10];

int dy[8] = { -1,-1,-1,1,1,1,0,0 };

int dx[8] = { -1,0,1,-1,0,1,-1,0 };

int nextFind(int by, int bx, int idx , char *word) {

// Rangecheck 기저함수를 잘체그하자

int nextidx = idx + 1;

if (by < 0 || by >= 5 || bx < 0 || bx >= 5) {

return 0;

}

if (nextidx >= strlen(word)) {

return 1;

}

if (strlen(word) == 1)

return 1;

for (int direction = 0; direction < 8; direction++) {

int nextY = by + dy[direction];

int nextX = bx + dx[direction];

if (map[nextY][nextX] == word[nextidx]) {

;

if (nextFind(nextY, nextX, nextidx , word))

return 1;

}

}

return 0;

}

int startWord(char *word) {

int bx, by;

for (by = 0; by < 5; by++) {

for (bx = 0; bx < 5; bx++) {

if (map[by][bx] = word[0]) {

if (nextFind(by, bx, 0 , word)) {

return 1;

}

}

}

}

return 0;

}

int main(void) {

int testcase , tc;

int i, j;

int num;

printf("input testcase : ");

scanf_s("%d", &testcase);

for (tc = 0; tc < testcase; tc++) {

for (i = 0; i < 5; i++) {

for (j = 0; j < 5; j++) {

printf("input %d by %d : ", i+1, j+1);

cin >> map[i][j];

}

}

printf("input num :");

scanf_s("%d", &num);

for (i = 0; i < num; i++) {

cin >> word;

if (startWord(word))

printf("%s : YES\n", word);

else

printf("%s : NO\n", word);

}

}

}

부가 설명을 하자면 dx와 dy는 좌표상에 이동을 할때

(-1,1) (0,1) (1,1)
(-1,0) (0,0)<- 현재 위치 (1,0)
(-1,-1) (0,-1) (1, -1)

이런식으로 좌표를 그린 것이라고 생각하면된다.

 

 

4. 시간 복잡도 분석

마지막 글자에 도달하기 저에 주변의 모든 칸에 대해 재귀 함수를 호출한다. 각 칸에는 최대 여덟개의 구성원이 있고 단어 길이가 N이라면 N-1단계 까지 진행된다. 따라서 시간복잡도는 O(8^(n-1))이다.

 

5. 후기..

평소에 코딩공부를 많이 안해서 나는 아직 멍청한가보다... 공부하자! 

재귀함수를 쓸 때는 항상 기저 조건을 생각하자! 

728x90
반응형