블록 게임
어떻게 풀까?
25개의 판 입니다! 게임의 상태를 나타내기 위해 '블록이 놓여있음'과 '놓여있지 않음' 두 가지로 나타난다고 해봅시다!
상태의 수는 2^25 = (2^10)^2*32이고, 32*e6 정도 되는 수이기 때문에 32메가 정도의 저장공간이 필요하겠군요!
다만, 함정이 하나 있습니다.
바로, int 는 4 바이트 라는 것이죠!! 아무 생각 없이 저장공간의 형식을 int로 했다간 런타임 오류가 뜰 겁니다!
573088 | BLOCKGAME | 상반기는꼭 | cpp | 1.9KB | 런타임 오류 | 23시간 전 | |
573087 | BLOCKGAME | 상반기는꼭 | cpp | 1.9KB | 런타임 오류 | 23시간 전 |
이처럼 말이죠!
자, 문제를 다시 한번 봅시다.
문제에서 요구하는 사항은 '이겼는지'와 '졌는지' 두 개 밖에 없습니다.
여기서, '아직 해결되지 않음'을 넣으면, 딱 3 가지의 상태만 필요하기 때문에 4 바이트나 잡아먹는 int 대신에
0~255까지의 256가지 상태를 저장할 수 있는 char를 사용해도 무방합니다! 심지어 char를 써도 굉장히 많은 공간이 남습니다....
이 점만 유의하시면 나머지는 어렵지 않습니다! 이전에 했던것과 비슷하게 문제를 풀면 되기 때문이죠.
블록을 놓을 수 있는지 없는지에 대해 검사를 하고, 놓을 수 있으면 놓은 상태를 비트로 만들어주는 함수를 만들고,
그 상태에서 자신이 이길 수 있는 경우가 하나라도 나오면 그 게임을 이길 수 있는 것이고, 블록을 놓을 수 없거나,
자신이 놓을 수 있는 블록의 상태에서 이길 수 있는 경우가 없다면 자신이 게임을 지게 되겠죠!
저같은 경우에는 BLOCK을 미리 만들어놓은 배열을 이용했습니다.
그리고, block이 놓여야 하는데 그 자리에 벽이 이미 세워져 있으면 블록을 놓지 못하게 했습니다.
만약 그렇지 않다면 블록을 놓았죠!
만약, 이에 대해 잘 모르겠다면 이전에 썼던 동적 계획법 - 6을 보시기 바랍니다! (클릭시 이동합니다.)
코드
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 | #include<cstdio> #include<cstring> char cache[1 << 25]; int conv[256]; char map[6][6] = { 0 }; const char *win[2] = { "LOSING", "WINNING" }; // 블럭들 const char block[6][2][2]{ { {1,1}, {1,0} }, { {1,1}, {0,1} }, { {0,1}, {1,1} }, { {1,0}, {1,1} }, { {1,1}, {0,0} }, { {1,0}, {1,0} } }; int ptb(int r, int c) { return 1 << (5 * r + c); } bool canWin(int state) { char &ret = cache[state]; if (ret != -1) return ret; ret = 0; //맵 체크 for (int r = 0; r < 5; r++) { for (int c = 0; c < 5; c++) { //블럭 6개 체크 for (int i = 0; i < 6; i++) { bool canBuild = true; //벽 놓을 수 있는지 체크 for (int k = 0; k < 2 && canBuild; k++) { for (int l = 0; l < 2; l++) { if (map[r + k][c + l] == '#' && block[i][k][l]) { canBuild = false; break; } } } //놓을 수 있으면 벽 놓기 if (canBuild) { int ns = state; for (int k = 0; k < 2 && canBuild; k++) { for (int l = 0; l < 2; l++) { if (block[i][k][l]) { map[r + k][c + l] = '#'; ns |= ptb(r + k, c + l); } } } ret = !canWin(ns); //다시 블럭 제거 for (int k = 0; k < 2 && canBuild; k++) { for (int l = 0; l < 2; l++) { if (block[i][k][l]) { map[r + k][c + l] = '.'; } } } //이길 수 있는 경우가 나오면 바로 return if (ret) return ret; } } } } return ret; } int main() { conv['.'] = 0; conv['#'] = 1; int t; scanf("%d\n", &t); memset(cache, -1, sizeof(cache)); while (t--) { int state = 0; for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { scanf(" \n%c", &map[i][j]); if (map[i][j] == '#') state |= ptb(i, j); } map[i][5] = '#'; map[5][i] = '#'; } map[5][5] = '#'; printf("%s\n", win[canWin(state)]); } return 0; } | cs |
여담
해당 문제는 이미 게임판의 크기가 결정되어 있습니다. 다만, 1 << 25의 부분문제를 가지고, 또 여기서 25 번의 체크를 하기 때문에 굉장히 느리다고 생각할 수 도있습니다! 하지만, 부분 문제가 1 << 25이긴 하더라도, 실제로 저 만큼의 경우의 수가 나오지 않습니다.
블럭이 3개 또는 2개로 뭉쳐있기 때문이죠!
종만북에 의하면 약 160만 개의 경우의 수가 나온다고 하는군요! 덕분에 그렇게 느리진 않습니다.
'공부 > 알고리즘 문제풀이' 카테고리의 다른 글
[알고스팟] 지니어스 (0) | 2018.07.16 |
---|---|
[알고스팟] 회전초밥 (2) | 2018.07.16 |
[알고스팟] 숫자 게임 (0) | 2018.07.16 |
[SW Expert Academy] 4050. 재관이의 대량 할인 (0) | 2018.07.13 |
[알고스팟] 틱택토 (2) | 2018.07.13 |