[BOJ] 백준 17281번 : ⚾ (JAVA)
문제의 링크 : https://www.acmicpc.net/problem/17281
문제
⚾는 9명으로 이루어진 두 팀이 공격과 수비를 번갈아 하는 게임이다. 하나의 이닝은 공격과 수비로 이루어져 있고, 총 N이닝 동안 게임을 진행해야 한다. 한 이닝에 3아웃이 발생하면 이닝이 종료되고, 두 팀이 공격과 수비를 서로 바꾼다.
두 팀은 경기가 시작하기 전까지 타순(타자가 타석에 서는 순서)을 정해야 하고, 경기 중에는 타순을 변경할 수 없다. 9번 타자까지 공을 쳤는데 3아웃이 발생하지 않은 상태면 이닝은 끝나지 않고, 1번 타자가 다시 타석에 선다. 타순은 이닝이 변경되어도 순서를 유지해야 한다. 예를 들어, 2이닝에 6번 타자가 마지막 타자였다면, 3이닝은 7번 타자부터 타석에 선다.
공격은 투수가 던진 공을 타석에 있는 타자가 치는 것이다. 공격 팀의 선수가 1루, 2루, 3루를 거쳐서 홈에 도착하면 1점을 득점한다. 타자가 홈에 도착하지 못하고 1루, 2루, 3루 중 하나에 머물러있을 수 있다. 루에 있는 선수를 주자라고 한다. 이닝이 시작될 때는 주자는 없다.
타자가 공을 쳐서 얻을 수 있는 결과는 안타, 2루타, 3루타, 홈런, 아웃 중 하나이다. 각각이 발생했을 때, 벌어지는 일은 다음과 같다.
- 안타: 타자와 모든 주자가 한 루씩 진루한다.
- 2루타: 타자와 모든 주자가 두 루씩 진루한다.
- 3루타: 타자와 모든 주자가 세 루씩 진루한다.
- 홈런: 타자와 모든 주자가 홈까지 진루한다.
- 아웃: 모든 주자는 진루하지 못하고, 공격 팀에 아웃이 하나 증가한다.
한 야구팀의 감독 아인타는 타순을 정하려고 한다. 아인타 팀의 선수는 총 9명이 있고, 1번부터 9번까지 번호가 매겨져 있다. 아인타는 자신이 가장 좋아하는 선수인 1번 선수를 4번 타자로 미리 결정했다. 이제 다른 선수의 타순을 모두 결정해야 한다. 아인타는 각 선수가 각 이닝에서 어떤 결과를 얻는지 미리 알고 있다. 가장 많은 득점을 하는 타순을 찾고, 그 때의 득점을 구해보자.
풀이
완전탐색과 구현을 이용한 문제였습니다. 타순에서 4번 타자는 항상 1번을 고정시킨다고 했으므로, 나머지 8명의 타자는 순열을 통해 모든 경우를 따져야했습니다. 어떤 두 명의 선수가 각각 2루타를 치고 홈런을 치는 것과, 홈런을 치고 2루타는 치는 것은 점수 차이가 나기때문에 조합이 아닌 순열을 사용해야합니다.
하나의 타순을 완성했다면, 이제 각 타자의 행동에 따라 야구를 진행하면 됩니다. 아웃 카운트가 3개일 때 종료된다는 점을 기억하면서 N번의 이닝만큼 반복문을 수행합니다.
야구를 진행하는 함수(Play 함수)는 따로 아래에서 더 자세히 알아보겠습니다.
Play 함수
이 함수를 설명하기 전에, 우리가 알아야할 주의 사항이 몇 가지 있습니다.
1. 이닝이 끝난다고 해서 1번 타자부터 타순이 진행되는 것이 아니다.
2. 각 이닝마다 베이스를 초기화해 주어야한다.
3. 만약, 한 이닝동안 i~9번 타자가 아웃을 3번 미만으로 당하고 안타만 쳤다면 1번 타자로 되돌려야한다.
4. 타자는 9명이다.
일단, 2번에서 말한 베이스는 홈, 1루, 2루, 3루에 해당합니다.
타자의 행동(0, 1, 2, 3, 4)에 따라서 switch ~ case문을 구현하면 됩니다.
0일 경우, 아웃 카운트를 늘리고,
1일 경우, 1루타이므로 3루 주자는 홈으로 들어오면서 점수를 획득하고 나머지는 1루씩 진루합니다.
2일 경우, 2루타이므로 2, 3루 주자는 홈으로 들어오면서 점수를 획득하고 나머지는 2루씩 진루합니다.
3일 경우, 3루타이므로 1, 2, 3루 주자는 홈으로 들어오면서 점수를 획득하고 나머지는 3루씩 진루합니다.
4일 경우, 홈런이므로 주자의 수 + 홈런 친 타자 만큼 점수를 획득하고 나머지 베이스는 비게 됩니다.
위 과정을 boolean 타입의 base를 선언하여, true와 false를 적절히 초기화하시면 됩니다.
만약, 중간에 아웃카운트가 3개가 되었다면 위 과정을 탈출하는 동시에 다음 이닝으로 넘기면 됩니다.
단, 1번 주의 사항에서 언급했듯이 다음 이닝으로 넘어갈 때는 현재 타자를 기준으로 다음 타자부터 시작해야 합니다.
예를 들어, 4번 타자에서 이닝이 종료되었다면, 다음 이닝에는 5번 타자부터 판단해 주어야 하는 것입니다.
또, 타자는 9명이기때문에 9번 타자에서 10번 타자로 넘어가는 것이 아니라, 1번 타자부터 시작해야 합니다.
마지막으로, 3번 주의 사항에 대해서 말씀드리자면, 위 과정은 이닝의 시작 타자부터 9번까지만 반복하는 것인데, 중간에 아웃 카운트가 3개가 되지 않은 채로 9번까지 진행된다면 다시 1번 타자부터 타격을 수행해야 하는 것입니다.
전반적인 설명은 여기까지 하고, 나머지는 코드의 주석을 차근 차근 읽어보시면서 이해하시면 되겠습니다.
소스코드
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.StringTokenizer;
public class Main {
static int N;
static int[][] players; // N번째 이닝에서 타자의 행동
static boolean[] select; // 아래 순열에서 사용될 boolean 타입 배열
static int[] lineUp; // 타순
static int ans;
public static void main(String[] args) throws NumberFormatException, IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
StringTokenizer st;
N = Integer.parseInt(br.readLine());
players = new int[N + 1][10];
for (int i = 1; i <= N; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 1; j <= 9; j++) {
players[i][j] = Integer.parseInt(st.nextToken());
}
}
select = new boolean[10];
lineUp = new int[10];
select[4] = true; // 4번 타자는 1번으로 고정되어있음.
lineUp[4] = 1;
ans = 0;
perm(2);
bw.write(ans + "\n");
bw.flush();
bw.close();
br.close();
}
// 순열
public static void perm(int num) {
if (num == 10) {
playBaseBall();
return;
}
for (int i = 1; i <= 9; i++) {
if (select[i]) {
continue;
}
select[i] = true;
lineUp[i] = num;
perm(num + 1);
select[i] = false;
}
}
// 야구 경기 실행
public static void playBaseBall() {
int score = 0;
int startPlayer = 1; // 이닝에서 처음 시작하는 타자
boolean[] base; // 홈, 1루, 2루, 3루를 표현.
for (int i = 1; i <= N; i++) { // N번째 이닝까지 실행 가능.
int outCnt = 0;
base = new boolean[4]; // base를 새롭게 초기화.
outer: while (true) {
for (int j = startPlayer; j <= 9; j++) {
int hitter = players[i][lineUp[j]]; // j번째 타자의 행동.
switch (hitter) {
case 0: // out.
outCnt++;
break;
case 1: // 1루타
for (int k = 3; k >= 1; k--) {
if (base[k]) {
if (k == 3) { // 3루에 있는 선수는 홈으로 들어옴.
score++; // 동시에 점수 획득.
base[k] = false; // 3루는 비어있게 됨.
continue;
}
// 1, 2루에 경우 1루씩 진루하고 원래 있던 자리는 비어있게 됨.
base[k] = false;
base[k + 1] = true;
}
}
base[1] = true; // 홈에서 1루로 진루.
break;
case 2: // 2루타
for (int k = 3; k >= 1; k--) {
if (base[k]) {
if (k == 3 || k == 2) { // 3루 혹은 2루에 있는 선수는 홈으로 들어옴.
score++; // 동시에 점수 획득.
base[k] = false; // 2루 또는 3루는 비어있게 됨.
continue;
}
// 1루에 경우 2루씩 진루하고 원래 있던 자리는 비어있게 됨.
base[k] = false;
base[k + 2] = true;
}
}
base[2] = true; // 홈에서 2루로 진루.
break;
case 3: // 3루타
for (int k = 3; k >= 1; k--) {
if (base[k]) { // 홈 제외 모든 선수는 홈으로 들어옴.
score++; // 동시에 점수 획득.
base[k] = false;
}
}
base[3] = true; // 홈에서 3루로 진루.
break;
case 4: // 홈런
for (int k = 1; k <= 3; k++) {
if (base[k]) { // 루상에 모든 주자가 홈으로 들어옴.
score++; // 주자 1명 당 점수 1점씩 획득
base[k] = false;
}
}
score++; // 홈런친 타자의 점수 1점 추가.
break;
}
if (outCnt == 3) { // 아웃 카운트가 3개 일 경우
// startPlayer를 그 다음 타자로 초기화 함.
startPlayer = j + 1;
if (startPlayer == 10) {
startPlayer = 1;
}
break outer;
}
}
// startPlayer를 1로 초기화해 주는 이유
// 1~9번까지 타자가 한 이닝에 전부 안타를 쳐서 아웃카운트가 발생하지 않게 되면,
// 위 반복문이 무한 루프를 돌기때문에 startPlayer = 1로 초기화해야 함.
startPlayer = 1;
}
}
ans = Math.max(ans, score);
}
}
|
cs |
지적 혹은 조언 환영합니다! 언제든지 댓글로 남겨주세요.
'PS > 백준' 카테고리의 다른 글
[BOJ] 백준 1937번 : 욕심쟁이 판다 (JAVA) (2) | 2020.05.29 |
---|---|
[BOJ] 백준 16637번 : 괄호 추가하기 (JAVA) (1) | 2020.05.28 |
[BOJ] 백준 17135번 : 캐슬 디펜스 (JAVA) (0) | 2020.05.26 |
[BOJ] 백준 17471번 : 게리멘더링 (JAVA) (0) | 2020.05.26 |
[BOJ] 백준 2331번 : 반복수열 (JAVA) (0) | 2020.05.25 |
댓글