-
[백준]16986: 인싸들의 가위바위보(java)Algorithm/백준 2022. 4. 8. 22:57728x90
https://www.acmicpc.net/problem/16986
문제
혹시 마지막으로 엠티를 간 것이 언제인가? 엠티를 안간지 꽤 오래됐다면 요즘 유행하는 인싸들의 가위바위보를 모를 것이다. 요즘 인싸들은 엠티에서 평범한 가위바위보를 시시하다는 이유로 더 이상 취급하지 않는다. 대신 가위불바위총번개악마용물공기보스펀지늑대나무사람뱀을 한다. 이 게임의 명칭이 다소 긴 관계로 문제 내에서는 전체 명칭을 적는 대신 이 게임의 또 다른 이름인 인싸 가위바위보로 부르겠다. 인싸 가위바위보는 평범한 가위바위보와 같이 각 손동작간의 상성이 정해져있다.
인싸 가위바위보는 평범한 가위바위보보다 흥미진진하고 재밌지만 3명 이상이 경기를 할 때 누가 이기고 누가 졌는지를 빠르게 알기 힘들다는 단점이 있다. 그렇기에 3명 이상의 사람들 사이에서 인싸 가위바위보를 할 때는 모두가 동시에 경기를 진행하는 대신 일대일 경기를 여러 번 진행해 누가 우승했는지 판단한다. 3명이서 인싸 가위바위보를 할 때의 우승자를 정하기 위한 구체적인 방식은 아래와 같다. 편의상 참가자 3명을 A, B, C라고 하자.
- A, B, C는 게임 시작 전 우승을 위해 필요한 승수와 경기 진행 순서를 미리 합의한다. 경기 진행 순서가 A, B, C라고 가정하자.
- 먼저 A와 B가 경기를 진행해 승자를 결정한다. 만약 두 사람이 같은 손동작을 내어 무승부가 발생할 경우 경기 진행 순서상 뒤인 사람이 이긴 것으로 간주한다. 즉 A와 B가 같은 손동작을 내면 B의 승리, A와 C가 같은 손동작을 내면 C의 승리, B와 C가 같은 손동작을 내면 C의 승리이다.
- 이전 경기의 승자와 이전 경기에 참여하지 않은 사람이 경기를 진행해 승자를 결정한다.
- 특정 사람이 미리 합의된 승수를 달성할 때 까지 3을 반복한다.
- 합의된 승수를 최초로 달성한 사람이 우승한다.
밑의 표는 침, 펄, 풍 세 사람이 인싸 가위바위보를 진행하는 예시이다. 우승을 위해 필요한 승수는 3승이고 침, 펄, 풍 순서로 경기를 진행한다.
인싸 가위바위보 결과 풍이 제일 먼저 3승에 도달했으므로 우승자는 풍이다. 1라운드, 3라운드에서 두 사람이 같은 손동작을 냈을 때 경기 진행 순서상 뒤인 사람이 승리하는 것을 확인할 수 있다.
컴퓨터공학과 새내기 지우는 첫 엠티에서 친구 경희, 민호와 인싸 가위바위보를 할 생각에 굉장히 신나있다. 지우는 경희와 민호의 행동 패턴을 빅데이터로 분석해 인싸 가위바위보를 하는 중 경희와 민호의 차례가 왔을 때 이들이 낼 손동작의 순서를 정확히 알고 있다. 그래서 마음만 먹으면 전승 우승이 가능하지만 경기를 흥미진진하게 이끌기 위해 인싸 가위바위보를 할 때 모든 손동작을 다르게 내고 싶어한다. 지우의 즐거운 대학생활을 위해 인싸 가위바위보의 상성표와 경희, 민호가 낼 손동작의 순서가 주어졌을 때 지우가 모든 손동작을 다르게 내어 우승할 수 있는지 판단하는 프로그램을 만들자. 경기 진행 순서는 지우, 경희, 민호 순으로 정해져있다.
입력
첫째 줄에 인싸 가위바위보의 손동작 수를 나타내는 N(1 ≤ N ≤ 9)과 우승을 위해 필요한 승수 K(1 ≤ K ≤ 6)가 한 칸의 빈칸을 사이에 두고 주어진다.
그 다음 N개의 줄에 대해 상성에 대한 정보 Ai,j가 주어진다. i+1번째 줄에는 N개의 정수 Ai,1, Ai,2, Ai,3, ..., Ai,N이 한 칸의 빈칸을 사이에 두고 주어진다. Ai,j가 2일 경우에는 i번 손동작이 j번 손동작을 이긴다는 의미이고, 1일 경우에는 비긴다는 의미이고, 0일 경우에는 진다는 의미이다. Ai,i = 1이고, i ≠ j일 때 Ai,j ≠ 1임이 보장된다. 또한 Ai,j가 2일 경우에는 Aj,i가 0이고, Ai,j가 0일 경우에는 Aj,i가 2임이 보장된다.
그 다음 줄에는 경희가 앞으로 자신이 참여하는 20경기에서 낼 손동작의 순서가 한 칸의 빈칸을 사이에 두고 주어진다. 손동작의 번호는 1 이상 N 이하이다.
그 다음 줄에는 민호가 앞으로 자신이 참여하는 20경기에서 낼 손동작의 순서가 한 칸의 빈칸을 사이에 두고 주어진다. 마찬가지로 손동작의 번호는 1 이상 N 이하이다.
출력
첫째 줄에 지우, 경희, 민호 순으로 경기를 진행할 때 지우가 모든 손동작을 다르게 내어 우승할 수 있으면 1을, 그렇지 않으면 0을 출력한다.
예제 입력 1
3 2 1 0 2 2 1 0 0 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
예제 출력 1
1
풀이
문제 이해하기가 너무 힘들었던 문제다.
1. 지우는 K번안에 이겨야한다.
2. 경희, 민호도 20번안에 이겨야한다.
3. 지우는 K번을 모두 다른 것을 내서 이겨야한다.
- 순열을 이용하였다. 이유는 1, 2, 3 과 3, 2, 1이 다르게 쳐져야하기 때문이다.
private static void Permutation(int cnt) { if(cnt == N) { if(playRockPaperScissors()) { System.out.println(1); System.exit(0); } return; } for(int i=0; i<N; i++) { if(visited[i]) continue; visited[i] = true; people[0][cnt] = i; Permutation(cnt+1); visited[i] = false; } }
4. 가위바위보 구현은 다음과같다.
- p1, p2가 게임을 한다.
- 0번은 지우, 1번은 경희, 2번은 민호일 때
- 지우가 K번 이기면 true 반환
- 만약 경희나 민호가 K번 이기면 false 반환
- 지우가 N번이상 내야하거나, 경희, 민호가 20번 이상 내야하면 false 반환
- 이것이 지켜진 상황에서 승자는 아래와 같은 함수로 구할 수 있다.
private static int getWinner(int p1, int p2, int[] idx) { int p1Turn = people[p1][idx[p1]]; int p2Turn = people[p2][idx[p2]]; if(map[p1Turn][p2Turn] == 2) { return p1; } else if(map[p1Turn][p2Turn] == 1) { return Math.max(p1, p2); } else { return p2; } }
- 후에 p3를 p2자리에 넣어주는데 이때 p3는 3 - p1 - p2로 구할 수 있다.
private static boolean playRockPaperScissors() { // 0 : 지우, 1 : 경희, 2 : 민호 int p1 = 0; int p2 = 1; int p3 = 2; int[] playerIdx = new int[3]; int[] winCnt = new int[3]; while(true) { p3 = 3-p1-p2; // 지우가 이김 if(winCnt[0] == K) { return true; } // 경희나 민호가 이김 if(winCnt[1] == K || winCnt[2] == K) { return false; } // 지우가 N번 이상 내야함, 경희, 민호가 20번 이상 내야함 if(playerIdx[0] == N || playerIdx[1] == 20 || playerIdx[2] == 20) { return false; } int winnerPlayer = getWinner(p1, p2, playerIdx); winCnt[winnerPlayer]++; playerIdx[p1]++; playerIdx[p2]++; p1 = winnerPlayer; p2 = p3; } }
전체코드
import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.Arrays; import java.util.StringTokenizer; public class BJ_G3_16986_인싸들의가위바위보 { static int N, K; static int[][] map; static int[][] people; static boolean[] visited; public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); K = Integer.parseInt(st.nextToken()); map = new int[N][N]; visited = new boolean[N]; for(int i=0; i<N; i++) { st = new StringTokenizer(br.readLine()); for(int j=0; j<N; j++) { map[i][j] = Integer.parseInt(st.nextToken()); } } people = new int[3][20]; for(int i=1; i<3; i++) { st = new StringTokenizer(br.readLine()); for(int j=0; j<20; j++) { people[i][j] = Integer.parseInt(st.nextToken())-1; } } Permutation(0); System.out.println(0); } private static void Permutation(int cnt) { if(cnt == N) { if(playRockPaperScissors()) { System.out.println(1); System.exit(0); } return; } for(int i=0; i<N; i++) { if(visited[i]) continue; visited[i] = true; people[0][cnt] = i; Permutation(cnt+1); visited[i] = false; } } private static boolean playRockPaperScissors() { // 0 : 지우, 1 : 경희, 2 : 민호 int p1 = 0; int p2 = 1; int p3 = 2; int[] playerIdx = new int[3]; int[] winCnt = new int[3]; while(true) { p3 = 3-p1-p2; // 지우가 이김 if(winCnt[0] == K) { return true; } // 경희나 민호가 이김 if(winCnt[1] == K || winCnt[2] == K) { return false; } // 지우가 N번 이상 내야함, 경희, 민호가 20번 이상 내야함 if(playerIdx[0] == N || playerIdx[1] == 20 || playerIdx[2] == 20) { return false; } int winnerPlayer = getWinner(p1, p2, playerIdx); winCnt[winnerPlayer]++; playerIdx[p1]++; playerIdx[p2]++; p1 = winnerPlayer; p2 = p3; } } private static int getWinner(int p1, int p2, int[] idx) { int p1Turn = people[p1][idx[p1]]; int p2Turn = people[p2][idx[p2]]; if(map[p1Turn][p2Turn] == 2) { return p1; } else if(map[p1Turn][p2Turn] == 1) { return Math.max(p1, p2); } else { return p2; } } }
728x90'Algorithm > 백준' 카테고리의 다른 글
[백준]21610: 마법사 상어와 비바라기(java) (0) 2022.04.10 [백준]2239: 스도쿠(java) (0) 2022.04.09 [백준]3190: 뱀(java) (0) 2022.04.07 [백준]16235: 나무 재테크(java) (0) 2022.04.06 [백준]16120: PPAP(java) (0) 2022.04.05