16986. 인싸들의 가위바위보 본문
문제링크 : https://www.acmicpc.net/problem/16986
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 123 | import java.util.Scanner; // 손가락 가지수가 최대 9개입니다. // 즉, 지우가 낼 수 있는 가지수가 9개 뿐이므로 아무리 많아도 9!개의 가지수가 존재하게 됩니다. // 따라서 재귀함수를 통한 백트래킹을 구현해도 아무런 문제가 없기 때문에 해당 방식으로 구현했습니다. // 시간복잡도 : O(N!) public class Main{ static boolean result = false; // 최종 결과 static int N, M; // N : 손가락 가지수 , M : 목표 승수 static int pick[][]; // 각 플레이어의 차례별로 낼 모양을 저장한 배열 static int arr[][]; // 각 손가락 모양별 상성을 저장할 배열 /////////////////////// user1과 user2가 낸 모양에 따라 승자의 번호를 리턴하는 함수 ////////////////////////// static int rsp(int pick1, int pick2, int user1, int user2) { if (pick1 != pick2) { if (arr[pick1][pick2] == 2) { return user1; } else if (arr[pick1][pick2] == 0) { return user2; } } else { return Math.max(user1, user2); } return -1; } /////////////////////// dfs를 시행할 함수 /////////////////////////// static void solution(int user1, int user2, int winCount[], int turnCount[], boolean used[]) { // user1과 user2는 유저의 번호, winCount는 해당 상태에서 각 플레이어의 승 수, turnCount는 해당 상황에서 각 플레이어가 몇번째 차례를 맞이했는지 // used는 모든 손가락 가지수에 대해 지우가 사용했는지 / 안했는지를 저장할 배열 // 지우가 먼저 목표 승수에 도달 if (winCount[0] == M) { result = true; return; } // 지우가 아닌 다른 사람이 먼저 목표 승수에 도달 else if (winCount[1] == M || winCount[2] == M) { return; } int userNext = 3 - user1 - user2; // 이번 게임이 끝나고 다음으로 만날 상대를 저장하는 변수 // 참가자 번호가 지우 : 0 , 경희 : 1, 민호 : 2이므로 비트마스크를 이용하여 다음 상대를 구할 수 있다. // user1에 지우가 있을 경우 / user2에 지우가 있을 경우 / 지우가 없을 경우를 나눠서 실행했습니다. // user1과 user2를 정렬한다면 user2가 지우인 경우를 확인할 수고를 덜 수 있습니다 ! if (user1 == 0) { // user1이 지우라면 for (int i = 1; i <= N; i++) { // 1번부터 모든 손가락 가지수에 대해 if (!used[i]) { // 사용하지 않은 것이 남아있다면 int winner = rsp(i, pick[user2][turnCount[user2]],0,user2); // user2가 이번 턴에 낼 손가락 종류와 아직 사용하지 않은 손가락 종류를 냈을 때 승자를 뽑습니다. winCount[winner]++; turnCount[user2]++; used[i] = true; solution(winner, userNext, winCount, turnCount, used); // 냈다는 가정하에 solution함수 실행 ! winCount[winner]--; turnCount[user2]--; used[i] = false; // 다시 안낸 상태로 복귀 ( 돌아올 경우 다른 손가락 가지수를 찾아서 실행하게 됩니다. ) } } } else if (user2 == 0) { // user2가 지우인 경우는 마찬가지 for (int i = 1; i <= N; i++) { if (!used[i]) { int winner = rsp(pick[user1][turnCount[user1]], i, user1, 0); winCount[winner]++; turnCount[user1]++; used[i] = true; solution(winner, userNext, winCount, turnCount, used); winCount[winner]--; turnCount[user1]--; used[i] = false; } } } else { // user1과 user2가 모두 지우가 아닐 때 int winner = rsp(pick[user1][turnCount[user1]], pick[user2][turnCount[user2]], user1, user2); // 각각 현재 낼 차례인 손가락을 이용하여 승자를 뽑습니다. winCount[winner]++; turnCount[user1]++; turnCount[user2]++; solution(winner, userNext, winCount, turnCount, used); // 주의할 점 ! 결국 이 함수도 끝에 도달할 경우 return되면서 돌아올 것이기 때문에 // 아래와 같이 반드시 원래 상태로 돌려줘야 합니다. (이것떄문에 맞왜틀..) winCount[winner]--; turnCount[user1]--; turnCount[user2]--; } } public static void main(String[] args) { Scanner scan=new Scanner(System.in); N=scan.nextInt(); M=scan.nextInt(); pick = new int[3][21]; arr=new int[12][12]; for (int i = 1; i <= N; i++) { for (int j = 1; j <= N; j++) { arr[i][j]=scan.nextInt(); } } for (int i = 0; i < 20; i++) { pick[1][i]=scan.nextInt(); } for (int i = 0; i < 20; i++) { pick[2][i]=scan.nextInt(); } int winCount[] = new int[3]; int turnCount[] = new int[3]; boolean used[] = new boolean[11]; solution(0, 1, winCount, turnCount, used); if(result) System.out.println(1); else System.out.println(0); } } | cs |
'알고리즘 문제풀기 > 백준(Acmicpc)' 카테고리의 다른 글
7569. 토마토 (0) | 2019.03.06 |
---|---|
16987. 계란으로 계란치기 (2) | 2019.03.02 |
1322. X와 K (0) | 2019.01.31 |
15711. 환상의 짝꿍 (2) | 2019.01.30 |
1124. 언더프라임 (0) | 2019.01.29 |
Comments