https://www.acmicpc.net/problem/16986
16986번: 인싸들의 가위바위보
두 사람이 같은 손동작을 내어 무승부가 발생할 경우 경기 진행 순서상 뒤인 사람이 이긴 것으로 간주함에 다시 한 번 유의한다. 구체적으로, 경기 진행 순서는 지우, 경희, 민호 순으로 고정되
www.acmicpc.net
코드설명
Simulation(시뮬레이션) + Normal Permutation (일반순열) 을 활용하는 문제입니다.
코드로직을 간단하게 설명해보면 다음과 같습니다.
- getJiwooSelected: 지우가 선택할 수 있는 모든 경우의 수를 재귀적으로 조합하여 검사합니다.
- startGame: 게임을 시작하고, 조건에 따라 승자를 판단합니다.
- winCheck: 게임 결과에 따라 승자를 결정하고, 게임 인덱스를 업데이트합니다.
문제에서 주어진대로 구현하면 되는 문제입니다.
문제에서 제가 겪었던 유의해야할점들입니다.
1. 지우가 모든 가위바위보를 진행하려면 반드시 gameTurn은 N 보다 작거나 같아야만 N까지 갈 수 있습니다.
처음에 N까지 이동하지 않고, gameTurn < N 으로 처리하여 N의 길이가 아닌, N-1의 까지 계산하여 한가지를 빼먹어서 틀렸었습니다.
while(gameTurn <= N) { //처음에 < N 으로 처리하여서 N이 3이라면, selected는 3이 나오는데 이떄 selected를 2까지밖에 검사하지 않아서 틀렸었습니다.
2. 다음 플레이어 효율적으로 구하기
int nextPlayerCalculate = 3 - (nextPlayer0 + nextPlayer1);
위의 코드를 통해 현재 선택되지 않은 플레이어의 Index를 바로 구할 수 있어, 많은 양의 코드가 획기적으로 줄었습니다.
이렇게 수학적인 계산방법은 인지하고 있어야만 합니다.
이러한 것들이 객체지향적으로 짜기 위해 필수적인 연산이라는 생각이 듭니다.
3. 문제에서 적절한 종료조건을 제시해야합니다.
예로 들었을떄 만약에 모든 가위바위보를 다 다르게 내면서 만약에 내기도 전에 이미 승리하는경우는?
아래와 같이 win==k일경우 바로 종료시킵니다.
또한 그러면서도, 지우가 이미 모두 다른 가위바위보를 냈는데도 불가할경우 종료시켜주어야합니다.
//이미 지우의 게임 플레이에는 모두 각각 다른 가위바위보가 들어가있습니다.
//그러므로 모든 승리가 완료된다면 이미 다른 각각의 가위바위보를 낸 것 입니다.
//또한 만약에 K가 0 일경우도 이를 통해 보완할 수 있습니다.
if(node[0].win == K) {
answer = 1;
return ;
}
//만약 지우가 모든 가위바위보를 내어놓고, 더이상 못낸다면 실패입니다.
if(node[0].playIndex == node[0].handArr.size()) {
// if(node[0].win == K) { //이 부분은 이미 앞에서 검증하여 제거해도 됩니다.
// answer = 1;
// return ;
// }else {
return ;
// }
}
4. 모든 연산 이후에는 원상복구를 해줌으로써, 이후의 함수 실행에 독립적으로 만들어줍니다.
연산이 진행된 후에 승리 수와 경기Index를 다시 원래대로 돌아오게 하여 다음의 실행에 영향을 주지 않도록 합니다.
else if(result == 0) {
player2.win += 1;
simulate(level + 1, player2.index, notparticipatePlayer);
player2.win -= 1;
}
//다음의 연산을 위한 원상복구가 필수적입니다.
player1.playIndex -= 1;
player2.playIndex -= 1;
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.StringTokenizer;
public class Main {
public static int N,M,K;
public static int[][] map;
public static Node[] node = new Node[3]; //지우, 경희, 민호 순서입니다.
public static int answer = 0;
public static boolean flag = false;
public static boolean[] visited;
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
K = Integer.parseInt(st.nextToken());
visited = new boolean[N];
map = new int[N][N];
//2 일 경우에는 i번 손동작이 j번 손동작을 이긴다는 의미이다.
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());
}
}
for(int i=0;i<3;i++) {
node[i] = new Node(i, 0, 0);
}
st = new StringTokenizer(br.readLine());
for(int i=0;i<20;i++) {
node[1].handArr.add(Integer.parseInt(st.nextToken()) - 1) ;
}
st = new StringTokenizer(br.readLine());
for(int i=0;i<20;i++) {
node[2].handArr.add(Integer.parseInt(st.nextToken()) - 1);
}
combination(0);
System.out.println(answer);
}
public static void combination(int level) {
if(level == N) {
// System.out.println("==============");
// for(int i=0;i<N;i++) {
// System.out.print(" "+node[0].handArr.get(i));
// }
// System.out.println();
if(answer == 0) {
simulate(0, 0, 1);
}
return ;
}
for(int i=0; i<N;i++) {
if(visited[i] == false) {
visited[i] = true;
node[0].handArr.add(i);
combination(level + 1);
node[0].handArr.remove(node[0].handArr.size() - 1);
visited[i] = false;
}
}
}
//경기 순서는 지우, 경희, 민호 순서이다.
public static void simulate(int level, int nextPlayer1, int nextPlayer2) {
//이미 지우의 게임 플레이에는 모두 각각 다른 플레이가 들어가있습니다.
if(node[0].win == K) {
answer = 1;
return ;
}
//만약 지우가 모든 플레이를 다 진행했다면,
if(node[0].playIndex == node[0].handArr.size()) {
// if(node[0].win == K) { //이 부분은 이미 앞에서 검증합니다.
// answer = 1;
// return ;
// }else {
return ;
// }
}
if(node[1].win == K || node[2].win == K) { //만약 다른사람이 이겼다면 종료시킵니다.
return ;
}
//(지우, 경희), (지우, 민호), (경희, 민호)
//다음 경기는 이긴사람과 참여안한사람이 경기합니다.
// System.out.println("nextPlayer1:"+nextPlayer1+" nextPlayer2:"+nextPlayer2);
Node player1 = node[nextPlayer1], player2 = node[nextPlayer2];
//지우가 참여하지 않는경우입니다.
//이제 가위바위보를 통해 누가 이길 것 인지 체크한다.
int hand1 = player1.handArr.get(player1.playIndex++);
int hand2 = player2.handArr.get(player2.playIndex++);
int result = map[hand1][hand2];
// System.out.println("result:"+result);
//player1이 이기는 경우입니다. player1과 아직 참여하지 않은 사람.
int notparticipatePlayer = 3 - ( player1.index + player2.index );
if(result == 2) {
player1.win += 1;
simulate(level + 1, player1.index, notparticipatePlayer);
player1.win -= 1;
}
//비길경우에는 진행순서상 뒤인 사람이 이긴것으로 간주한다.
//즉, index가 큰 사람이 이긴것으로 작동시킨다.
else if(result == 1) {
int drawPlayer = Math.max(player1.index, player2.index);
node[drawPlayer].win += 1;
simulate(level + 1, drawPlayer, notparticipatePlayer);
node[drawPlayer].win -= 1;
}
else if(result == 0) {
player2.win += 1;
simulate(level + 1, player2.index, notparticipatePlayer);
player2.win -= 1;
}
player1.playIndex -= 1;
player2.playIndex -= 1;
}
}
class Node{
int index;
int win;
int playIndex = 0;
ArrayList<Integer> handArr = new ArrayList<Integer>();
public Node(int index, int win, int playIndex) {
this.index = index;
this.win = win;
this.playIndex = playIndex;
}
}
객체지향적으로 접근해본 코드.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.StringTokenizer;
public class Main {
public static int N,M,K;
public static int[][] map;
public static Node[] node = new Node[3]; //지우, 경희, 민호 순서입니다.
public static int answer = 0;
public static boolean flag = false;
public static boolean[] visited;
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
K = Integer.parseInt(st.nextToken());
visited = new boolean[N];
map = new int[N][N];
//2 일 경우에는 i번 손동작이 j번 손동작을 이긴다는 의미이다.
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());
}
}
for(int i=0;i<3;i++) {
node[i] = new Node(i, 0, 0);
}
st = new StringTokenizer(br.readLine());
for(int i=0;i<20;i++) {
node[1].handArr.add(Integer.parseInt(st.nextToken()) - 1) ;
}
st = new StringTokenizer(br.readLine());
for(int i=0;i<20;i++) {
node[2].handArr.add(Integer.parseInt(st.nextToken()) - 1);
}
combination(0);
System.out.println(answer);
}
public static void combination(int level) {
if(level == N) {
if(answer == 0) {
simulate(0, 0, 1);
}
return ;
}
for(int i=0; i<N;i++) {
if(visited[i] == false) {
visited[i] = true;
node[0].handArr.add(i);
combination(level + 1);
node[0].handArr.remove(node[0].handArr.size() - 1);
visited[i] = false;
}
}
}
//경기 순서는 지우, 경희, 민호 순서이다.
public static void simulate(int level, int nextPlayer1, int nextPlayer2) {
//이미 지우의 게임 플레이에는 모두 각각 다른 가위바위보가 들어가있습니다.
//그러므로 모든 승리가 완료된다면 이미 다른 각각의 가위바위보를 낸 것 입니다.
//또한 만약에 K가 0 일경우도 이를 통해 보완할 수 있습니다.
if(node[0].win == K) {
answer = 1;
return ;
}
//만약 지우가 모든 가위바위보를 내어놓고, 더이상 못낸다면 실패입니다.
if(node[0].playIndex == node[0].handArr.size()) {
// if(node[0].win == K) { //이 부분은 이미 앞에서 검증하여 제거해도 됩니다.
// answer = 1;
// return ;
// }else {
return ;
// }
}
if(node[1].win == K || node[2].win == K) { //만약 다른사람이 이겼다면 종료시킵니다.
return ;
}
//(지우, 경희), (지우, 민호), (경희, 민호)
//다음 경기는 이긴사람과 참여안한사람이 경기합니다.
Node player1 = node[nextPlayer1], player2 = node[nextPlayer2];
//이제 가위바위보를 통해 누가 이길 것 인지 체크할 것 입니다.
int hand1 = player1.handArr.get(player1.playIndex++);
int hand2 = player2.handArr.get(player2.playIndex++);
int result = map[hand1][hand2];
//player1이 이기는 경우입니다. player1과 아직 참여하지 않은 사람.
int notparticipatePlayer = 3 - ( player1.index + player2.index );
if(result == 2) {
player1.win += 1;
simulate(level + 1, player1.index, notparticipatePlayer);
player1.win -= 1;
}
//비길경우에는 진행순서상 뒤인 사람이 이긴것으로 간주한다.
//즉, index가 큰 사람이 이긴것으로 작동시킨다.
else if(result == 1) {
int drawPlayer = Math.max(player1.index, player2.index);
node[drawPlayer].win += 1;
simulate(level + 1, drawPlayer, notparticipatePlayer);
node[drawPlayer].win -= 1;
}
else if(result == 0) {
player2.win += 1;
simulate(level + 1, player2.index, notparticipatePlayer);
player2.win -= 1;
}
//다음의 연산을 위한 원상복구가 필수적입니다.
player1.playIndex -= 1;
player2.playIndex -= 1;
}
}
class Node{
int index;
int win;
int playIndex = 0;
ArrayList<Integer> handArr = new ArrayList<Integer>();
public Node(int index, int win, int playIndex) {
this.index = index;
this.win = win;
this.playIndex = playIndex;
}
}
다음 플레이어를 3 - (nextPlayer0 + nextPlayer1) 을 통해 빠르게 구하여 코드양을 줄였습니다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.StringTokenizer;
public class Main {
public static int N, M, K;
public static int[] arr;
public static int[][] A;
public static int[][] hand;
public static int[] selected;
public static boolean[] visited;
public static int answer = 0;
public static int jiwoo = 0, gyung = 1, minho = 2;
public static int nextPlayer0 = jiwoo, nextPlayer1 = gyung; //다음에 할 플레이어는 ?
public static int nextPlayerHand0 = 0;
public static int nextPlayerHand1 = 0;
public static int[] winCnt = new int[3]; //각 플레이어별로 몇번 이겼는가 ?
public static int[] gameIdx = new int[3]; // 각 플레이어의 게임횟수.
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
K = Integer.parseInt(st.nextToken());
A = new int[N][N];
hand = new int[3][20];
for(int i=0;i<N;i++) {
st = new StringTokenizer(br.readLine());
for(int j=0;j<N;j++) {
A[i][j] = Integer.parseInt(st.nextToken());
}
}
for(int i=1;i<3;i++) {
st = new StringTokenizer(br.readLine());
for(int j=0;j<20;j++) {
hand[i][j] = Integer.parseInt(st.nextToken()) - 1;
}
}
visited = new boolean[N];
selected = new int[N];
getJiwooSelected(0);
System.out.println(answer);
}
public static void getJiwooSelected(int level) {
if(level == N) {
//각 순서별로 이제 경기를 진행한다.
if( startGame() == true) {
answer = 1;
}
return ;
}
for(int i=0;i<N;i++) {
if(visited[i] == false) {
visited[i] = true;
hand[0][level] = i;
getJiwooSelected(level + 1);
visited[i] = false;
}
}
}
public static boolean startGame() {
winCnt = new int[3]; //각 플레이어별로 몇번 이겼는가 ?
gameIdx = new int[3]; // 각 플레이어의 게임횟수.
//게임 순서는 지우, 경희, 민호 순 입니다.
jiwoo = 0; gyung = 1; minho = 2;
nextPlayer0 = jiwoo; nextPlayer1 = gyung; //다음에 할 플레이어는 ?
nextPlayerHand0 = hand[nextPlayer0][gameIdx[nextPlayer0]];
nextPlayerHand1 = hand[nextPlayer1][gameIdx[nextPlayer1]];
int gameTurn = 0;
while(gameTurn <= N) { //처음에 < N 으로 처리하여서 N이 3이라면, selected는 3이 나오는데 이떄 selected를 2까지밖에 검사하지 않아서 틀렸었습니다.
gameTurn += 1;
nextPlayerHand0 = hand[nextPlayer0][gameIdx[nextPlayer0]];
nextPlayerHand1 = hand[nextPlayer1][gameIdx[nextPlayer1]];
//2 라면 nextPlayerHand0 가 nextPlayerHand1 을 이긴다는 의미입니다.
if(A[nextPlayerHand0][nextPlayerHand1] == 2) {
//nextPlayerHand1을 교체해줍니다.
winCheck(2);
}
//만약 1 이라면 경기순서가 더 뒤에있는 사람이 승리합니다.
else if(A[nextPlayerHand0][nextPlayerHand1] == 1) {
winCheck(1);
}
//만약 0 이라면, 진 것 입니다. nextPlayerHand0을 교체해줍니다.
else {
winCheck(0);
}
//만약 jiwoo의 승자횟수가 K라면 지우가 승리입니다.
if(winCnt[gyung] == K || winCnt[minho] == K)
return false;
if( winCnt[jiwoo] == K) {
return true;
}
}
//만약 jiwoo의 승자횟수가 K라면 지우가 승리입니다.
if(winCnt[gyung] == K || winCnt[minho] == K)
return false;
if( winCnt[jiwoo] == K) {
return true;
}
return false;
}
public static void winCheck(int type) {
gameIdx[nextPlayer0] += 1;
gameIdx[nextPlayer1] += 1;
int nextPlayerCalculate = 3 - (nextPlayer0 + nextPlayer1);
//nextPlayer0 가 이긴경우입니다.
if(type == 2) {
winCnt[nextPlayer0] += 1;
nextPlayer1 = nextPlayerCalculate;
}
//서로 비긴경우, 경기진행순서가 뒤인 사람이 이긴것으로 처리합니다.
else if(type == 1) {
//만약 nextPlayer0가 더 작다면, nextPlayer1 이 이긴 것 입니다.
if(nextPlayer0 < nextPlayer1) {
winCnt[nextPlayer1] += 1;
nextPlayer0 = nextPlayerCalculate;
}
//만약 nextPlayer0이 더 크다면, nextPlayer0 이 이긴 것 입니다.
else if(nextPlayer0 > nextPlayer1) {
winCnt[nextPlayer0] += 1;
nextPlayer1 = nextPlayerCalculate;
}
}
else if(type == 0) {
winCnt[nextPlayer1] += 1;
nextPlayer0 = nextPlayerCalculate;
}
}
}
처음에 푼 코드입니다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.StringTokenizer;
public class Main {
public static int N, M, K;
public static int[] arr;
public static int[][] A;
public static int[][] hand;
public static int[] selected;
public static boolean[] visited;
public static int answer = 0;
public static int jiwoo = 0, gyung = 1, minho = 2;
public static int nextPlayer0 = jiwoo, nextPlayer1 = gyung; //다음에 할 플레이어는 ?
public static int nextPlayerHand0 = 0;
public static int nextPlayerHand1 = 0;
public static int[] winCnt = new int[3]; //각 플레이어별로 몇번 이겼는가 ?
public static int[] gameIdx = new int[3]; // 각 플레이어의 게임횟수.
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
K = Integer.parseInt(st.nextToken());
A = new int[N][N];
hand = new int[3][20];
for(int i=0;i<N;i++) {
st = new StringTokenizer(br.readLine());
for(int j=0;j<N;j++) {
A[i][j] = Integer.parseInt(st.nextToken());
}
}
for(int i=1;i<3;i++) {
st = new StringTokenizer(br.readLine());
for(int j=0;j<20;j++) {
hand[i][j] = Integer.parseInt(st.nextToken()) - 1;
}
}
visited = new boolean[N];
selected = new int[N];
getJiwooSelected(0);
System.out.println(answer);
}
public static void getJiwooSelected(int level) {
if(level == N) {
//각 순서별로 이제 경기를 진행한다.
if( startGame() == true) {
answer = 1;
}
return ;
}
for(int i=0;i<N;i++) {
if(visited[i] == false) {
visited[i] = true;
hand[0][level] = i;
getJiwooSelected(level + 1);
visited[i] = false;
}
}
}
public static boolean startGame() {
winCnt = new int[3]; //각 플레이어별로 몇번 이겼는가 ?
gameIdx = new int[3]; // 각 플레이어의 게임횟수.
//게임 순서는 지우, 경희, 민호 순 입니다.
jiwoo = 0; gyung = 1; minho = 2;
nextPlayer0 = jiwoo; nextPlayer1 = gyung; //다음에 할 플레이어는 ?
nextPlayerHand0 = hand[nextPlayer0][gameIdx[nextPlayer0]];
nextPlayerHand1 = hand[nextPlayer1][gameIdx[nextPlayer1]];
int gameTurn = 0;
while(gameTurn <= N) { //처음에 < N 으로 처리하여서 N이 3이라면, selected는 3이 나오는데 이떄 selected를 2까지밖에 검사하지 않아서 틀렸었습니다.
gameTurn += 1;
nextPlayerHand0 = hand[nextPlayer0][gameIdx[nextPlayer0]];
nextPlayerHand1 = hand[nextPlayer1][gameIdx[nextPlayer1]];
//2 라면 nextPlayerHand0 가 nextPlayerHand1 을 이긴다는 의미입니다.
if(A[nextPlayerHand0][nextPlayerHand1] == 2) {
//nextPlayerHand1을 교체해줍니다.
winCheck(2);
}
//만약 1 이라면 경기순서가 더 뒤에있는 사람이 승리합니다.
else if(A[nextPlayerHand0][nextPlayerHand1] == 1) {
winCheck(1);
}
//만약 0 이라면, 진 것 입니다. nextPlayerHand0을 교체해줍니다.
else {
winCheck(0);
}
//만약 jiwoo의 승자횟수가 K라면 지우가 승리입니다.
if(winCnt[gyung] == K || winCnt[minho] == K)
return false;
if( winCnt[jiwoo] == K) {
return true;
}
}
//만약 jiwoo의 승자횟수가 K라면 지우가 승리입니다.
if(winCnt[gyung] == K || winCnt[minho] == K)
return false;
if( winCnt[jiwoo] == K) {
return true;
}
return false;
}
public static void winCheck(int type) {
gameIdx[nextPlayer0] += 1;
gameIdx[nextPlayer1] += 1;
//nextPlayer0 가 이긴경우입니다.
if(type == 2) {
winCnt[nextPlayer0] += 1;
if( (nextPlayer0 == jiwoo && nextPlayer1 == gyung) || (nextPlayer0 == gyung && nextPlayer1 == jiwoo)) {
nextPlayer1 = minho;
}
else if( (nextPlayer0 == jiwoo && nextPlayer1 == minho) || (nextPlayer0 == minho && nextPlayer1 == jiwoo) ) {
nextPlayer1 = gyung;
}
else {
nextPlayer1 = jiwoo;
}
}
//서로 비긴경우, 경기진행순서가 뒤인 사람이 이긴것으로 처리합니다.
else if(type == 1) {
//만약 nextPlayer0가 더 작다면, nextPlayer1 이 이긴 것 입니다.
if(nextPlayer0 < nextPlayer1) {
winCnt[nextPlayer1] += 1;
if( (nextPlayer0 == jiwoo && nextPlayer1 == gyung) || (nextPlayer0 == gyung && nextPlayer1 == jiwoo)) {
nextPlayer0 = minho;
}
else if( (nextPlayer0 == jiwoo && nextPlayer1 == minho) || (nextPlayer0 == minho && nextPlayer1 == jiwoo) ) {
nextPlayer0 = gyung;
}
else {
nextPlayer0 = jiwoo;
}
}
//만약 nextPlayer0이 더 크다면, nextPlayer0 이 이긴 것 입니다.
else if(nextPlayer0 > nextPlayer1) {
winCnt[nextPlayer0] += 1;
if( (nextPlayer0 == jiwoo && nextPlayer1 == gyung) || (nextPlayer0 == gyung && nextPlayer1 == jiwoo)) {
nextPlayer1 = minho;
}
else if( (nextPlayer0 == jiwoo && nextPlayer1 == minho) || (nextPlayer0 == minho && nextPlayer1 == jiwoo) ) {
nextPlayer1 = gyung;
}
else {
nextPlayer1 = jiwoo;
}
}
}
else if(type == 0) {
winCnt[nextPlayer1] += 1;
if( (nextPlayer0 == jiwoo && nextPlayer1 == gyung) || (nextPlayer0 == gyung && nextPlayer1 == jiwoo)) {
nextPlayer0 = minho;
}
else if( (nextPlayer0 == jiwoo && nextPlayer1 == minho) || (nextPlayer0 == minho && nextPlayer1 == jiwoo) ) {
nextPlayer0 = gyung;
}
else {
nextPlayer0 = jiwoo;
}
}
}
}