숫자 게임
어떻게 풀까?
두 명이 다 최선을 다해서 게임을 다합니다!
즉, 게임의 한 상황이 있으면, 자신이 선택하는 수는 다음 상황에서 가장 차이가 큰 수를 고를 것 입니다!
만약 어떻게 다음 상황에서 큰 수를 고르는지 궁금하시다면, 이전에 썼던 동적 계획법 강의를 봐보세용!!
또한, 왼쪽과 오른쪽에서만 뽑기 때문에 게임의 상태를 가장 왼쪽에 있는
인덱스 left와 가장 오른쪽에 있는 인덱스 right를 이용해서 나타낼 수 있습니다!
이렇게 하면, 현재 턴이 누구냐와 부분 집합 score[left][right] 배열을 이용하면, 현재 상태에서 가장 큰 점수가 몇점 인지 구할 수 있습니다.
그리고 해당 상태에서 차례인 사람이 얻을 수 있는 최대 점수를 diff(turn, l, r)이라고 합시다!
(저같은 경우는 현우가 얻을 수 있는 큰 점수는 +로 나타냈고, 서하가 얻을 수 있는 점수는 -로 나타냈습니다.)
그러면 dif(turn, l, r)의 상태는 다음 4 가지의 부분 집합 중 최대를 고르면 됩니다!
(점수판의 점수 정보는 price[]에 있다고 하겠습니다.)
위에는 max라고 썼지만, 저같은 경우는 서하가 얻을 수 있는 가장 큰 점수는 -로 나타냈기 때문에,
서하의 경우에는 min값을 고르면 될 것입니다!
+price[]도 서하의 경우에는 -price[]가 되겠죠.
코드
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 | #include<cstdio> #include<cstring> #define INF 987654321 enum TURN {H = 0, S = 1}; int cache[50][50][2], prices[50]; int max(int i1, int i2) { return i1 > i2 ? i1 : i2; } int min(int i1, int i2) { return i1 < i2 ? i1 : i2; } int same(int i1) { return i1; } int mi(int i1) { return -i1; } int(*cal[2])(int, int) = { max, min }; int(*conv[2])(int i1) = { same, mi }; int rets[2] = { -INF, INF }; int diff(int turn, int l, int r) { if (l > r) { return 0; } int &ret = cache[l][r][turn]; if (ret != -1) return ret; ret = rets[turn]; //cal[] : 서하의 경우엔 - 점수가 큰 점수, 따라서 min을 이용해 계산 // 현우의 경우엔 + 점수가 큰 점수, 따라서 max를 이용해 계산 //conv[] : 서하의 경우엔 점수를 얻으면 - 실행 // 현우의 경우엔 점수를 얻으면 + 실행 ret = cal[turn](ret, diff(turn ^ 1, l + 1, r) + conv[turn](prices[l])); // 왼쪽꺼 하나 떼기 ret = cal[turn](ret, diff(turn ^ 1, l, r - 1) + conv[turn](prices[r])); //오른쪽꺼 하나 빼기 if (r != l) { ret = cal[turn](ret, diff(turn ^ 1, l + 2, r)); //왼쪽꺼 두개 떼기; ret = cal[turn](ret, diff(turn ^ 1, l, r - 2)); // 오른쪽꺼 두개 빼기 } return ret; } int main(){ int t; scanf("%d\n", &t); while (t--) { memset(cache, -1, sizeof(cache)); int n; scanf("%d\n", &n); for (int i = 0; i < n; i++) { scanf("%d \n", &prices[i]); } int res = diff(H, 0, n - 1); printf("%d\n", res); } return 0; } | cs |
여담
이 문제를 다시보니, 뺄 때에도 2개를 연달아 빼고, 서로 하나씩 밖에 빼지 못하기 때문에
상태에 따라서 누가 플레이하는지가 결정된다고 하더군요!
뭔가 허무했습니다.
그리고 위와 같은 문제는 제가 풀었던 방법에서 보이듯이,
서하는 결과를 최소화 하기 위해 플레이하고, 현우는 결과를 최대화 하기 위해서 플레이를 합니다.
트리의 층마다 최대화와 최소화가 번갈아 나타난다는 의미에서 이 알고리즘을 미니맥스 알고리즘이라고 합니다!
인공지능에서 배운다고 하더군요.. -출처 : 종만북
시간복잡도는 부분 문제가 n^2이고, 부분 문제마다 4개의 연산을 하기 때문에 가 됩니다!
'공부 > 알고리즘 문제풀이' 카테고리의 다른 글
[알고스팟] 회전초밥 (2) | 2018.07.16 |
---|---|
[알고스팟] 블록 게임 (0) | 2018.07.16 |
[SW Expert Academy] 4050. 재관이의 대량 할인 (0) | 2018.07.13 |
[알고스팟] 틱택토 (2) | 2018.07.13 |
[SW Expert Academy] Code Battle! (0) | 2018.07.11 |