틱택토
어떻게 풀까?
우선, 조합 게임의 특성을 알아야 합니다!
이전에 썼던 설명을 이용하겠습니다!
위 그림처럼, 마지막에 게임의 결과가 나올 것입니다. 이를 통해서, 전 단계가 A 차례 였다면, A는 무조건 A가 이기는 결과를 선택할 것입니다.
그렇다면, A 차례인 위 단계에서는 A가 이기는 결과가 될 것입니다.
이와 다르게 B차례에서 A가 이기는 결과로밖에 갈 수 없다면, B 차례의 상태에서는 A가 승리한다는 결과가 될 것입니다.
위와 같은 방식으로 한다면, 틱택토의 상태에 따라서 결과를 정할 수 있습니다.
다만, 틱택토에서는 비기는 결과도 있다는 것을 잊지 말아야 합니다!
그렇다면, 맵의 상태에 따라서 현재 차레가 'o'냐 'x'이냐에 따라서 다음과 같은 선택을 하게 됩니다.
1. 현재 상태와 다음 상태에서의 승리자가 같은 경우 : 현재 상태자의 승리로 현재 상태 끝
2. 현재 상태와 다음 상태에서가 비길 경우 : 지는 경우였을 경우 비기는 상태로 변경
3. 현재 상태와 다음 상태에서 질 경우 : 비기는 상태일 경우 비긴 상태로 하고, 지는 상태일 경우 변화 없음.
그리고, 이전에 어떤 경로로 'o'나 'x'가 놓여진건 상관 없이, 현재 말이 놓여진 상태만이 결과에 영향을 줍니다.
심지어, 틱택토의 'o'차례인지 'x'차례인지도 틱택토의 현재 맵 상태에 따라서 결정됩니다!
('x'가 먼저이기 때문이죠. 정확히는 빈 곳의 개수가 홀수 이면 x의 차례, 짝수개이면 o의 차례입니다.)
따라서, 메모이제이션은 그냥 맵의 현재 상황으로 저장할 수 있습니다!
그렇다면... 이제 중요한건 맵의 현재 상황을 어떻게 표현할 것인가가 되겠죠
하지만! 맵의 크기는 3x3의 9칸 밖에 없습니다.
또한, 맵의 상태는 아무것도 놓여진 '.'과 o가 놓여진 'o', x가 놓여진 'x' 3 가지로 표현이 가능합니다.
그렇다면 3 진법을 사용해서 맵을 나타낼 수 있겠죠!
메모리도 3^9로 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 | #include<cstdio> #include<cstring> char memoi[20000]; char map[3][4]; int conv[256], loser[256]; bool win() { bool res = false; for (int i = 0; i < 3; i++) { res |= map[i][0] != '.' && map[i][0] == map[i][1] && map[i][1] == map[i][2]; } for (int j = 0; j < 3; j++) { res |= map[0][j] != '.' && map[0][j] == map[1][j] && map[0][j] == map[2][j]; } res |= map[1][1] != '.' && map[0][0] == map[1][1] && map[0][0] == map[2][2]; res |= map[1][1] != '.' && map[2][0] == map[1][1] && map[2][0] == map[0][2]; return res; } int getState() { int state = 0; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { state *= 3; state += conv[map[i][j]]; } } return state; } char winner(int depth, char turn) { char &ret = memoi[getState()]; if (ret != -1) return ret; if (win()) { return ret = loser[turn]; } if (depth == 9) return ret = 0; ret = loser[turn]; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (map[i][j] == '.') { map[i][j] = turn; char w = winner(depth + 1, loser[turn]); map[i][j] = '.'; if (w == turn) { ret = turn; return ret; } else if (w == 0) { ret = 0; } } } } return ret; } int main() { int t; scanf("%d\n", &t); memset(memoi, -1, sizeof(memoi)); conv['.'] = 0; conv['o'] = 1; conv['x'] = 2; loser['o'] = 'x'; loser['x'] = 'o'; int depth = 0; for (int tc = 1; tc <= t; tc++) { for (int i = 0; i < 3; i++) { scanf("%s\n", map[i]); } for(int i = 0; i < 3; ++i){ for(int j = 0; j <3; ++j){ depth += map[i][j] != '.'; } } int cnt = 0; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { cnt += map[i][j] == '.'; } } char res = winner(depth, cnt & 1 ? 'x' : 'o'); if (res == 0) printf("TIE\n"); else { printf("%c\n", res); } } return 0; } | cs |
여담
시간 복잡도는 어떨까요?
메모이제이션이 3^9이고, 각 행마다 3^2 의 반복문을 가지기 때문에
3^11의 시간복잡도를 가질것입니다!
무려 O(1) 입니다.
'공부 > 알고리즘 문제풀이' 카테고리의 다른 글
[알고스팟] 숫자 게임 (0) | 2018.07.16 |
---|---|
[SW Expert Academy] 4050. 재관이의 대량 할인 (0) | 2018.07.13 |
[SW Expert Academy] Code Battle! (0) | 2018.07.11 |
[SW Expert Acade] 4616. 점프점프! 개굴이의 점핑! (0) | 2018.07.05 |
[알고스팟] 실험 데이터 복구하기 (0) | 2018.07.04 |