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을 출력한다.
지우/경희/민호가 인싸들의 가위바위보를 하는데 지우가 이길 수 있는지 없는지 출력하는 문제이다.
이 문제를 잘 못 읽고 코드를 잘못짜서 총 3시간은 날린 것 같다. 문제를 바로 잡고 처음부터 다시 코드 써서 제출하는데 30분밖에 안걸렸는데 3시간을 날린건,, 집중해서 맨 처음에 문제를 바로 읽고 올바른 코드를 작성하는 것에 늘 집중해야겠다.
지우가 낼 수 있는 손동작 수는 전에 내지 않았던 다른 손동작이어야한다.
손동작의 수는 최대값이 9이기 때문에 모든 경우의 수를 완전탐색을 통해 확인해보아도 충분히 시간 안에 확인해볼 수 있다. 그래서 나는 C++의 함수 next_permutation을 통해 모든 순서의 경우의 수를 확인하는 방법으로 가능한지 불가능한지 확인하는 코드를 작성했다.
그리고 재귀함수로 게임이 아예 끝나거나, 누군가의 우승으로 끝나는 걸 기저사례로 설정한 후, 한 번씩 경기를 치르는 것을 코드로 작성하였다.
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <cmath>
#define INF 9876543210
using namespace std;
// BOJ :: https://www.acmicpc.net/problem/16986
// 경기 진행 순서는 지우, 경희, 민호 순으로 정해져있음
int N, FORWIN;
int ps1 = 1, ps2 = 1, ps3 = 1;
bool isCan = false;
bool visited[10];
int R[10][10];
int WHO[4][22];
int DP[10];
vector<int> v[4];
void input() {
cin >> N >> FORWIN;
for (int i = 1; i <= N; i++) DP[i] = i;
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++)
cin >> R[i][j];
for (int i = 2; i <= 3; i++)
for (int round = 1; round <= 20; round++)
cin >> WHO[i][round];
}
bool isEnd() {
for (int i = 1; i <= 3; i++) {
if (v[i].size() >= FORWIN) {
if (i == 1) isCan = true;
return true;
}
}
return false;
}
void go(int p1, int p2, int p3, int A, int B) {
if (isEnd()) return;
if (p1 > N || p2 > 20 || p3 > 20) return;
int isWin, Another;
if (A == 1) {
if (B == 2) {
Another = 3;
isWin = R[DP[p1++]][WHO[B][p2++]];
}
else {
Another = 2;
isWin = R[DP[p1++]][WHO[B][p3++]];
}
if (isWin == 0 || isWin == 1) {
v[B].push_back(1);
go(p1, p2, p3, 2, 3);
}
else {
v[1].push_back(1);
go(p1, p2, p3, 1, Another);
}
}
else {
isWin = R[WHO[2][p2++]][WHO[3][p3++]];
if (isWin == 0 || isWin == 1) {
v[3].push_back(1);
go(p1, p2, p3, 1, 3);
}
else {
v[2].push_back(1);
go(p1, p2, p3, 1, 2);
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
input();
do {
ps1 = ps2 = ps3 = 1;
go(ps1, ps2, ps3, 1, 2);
if (isCan) {
cout << 1 << '\n';
return 0;
}
for (int i = 1; i <= 3; i++)
v[i].clear();
} while (next_permutation(DP + 1, DP + N + 1));
cout << 0 << '\n';
return 0;
}
'BOJ > 시물레이션' 카테고리의 다른 글
[C/C++] 백준 - 19237번 : 어른 상어 (0) | 2021.08.05 |
---|---|
[C/C++] 백준 - 17822번 : 원판 돌리기 (0) | 2021.08.04 |
[C/C++] 백준 - 10836번 : 여왕벌 (0) | 2021.07.30 |
[C/C++] 백준 - 17406번 : 배열 돌리기4 (0) | 2021.07.29 |
[C/C++] 백준 - 20058번 : 마법사 상어와 파이어스톰 (0) | 2021.07.29 |