[BOJ] - ⚾(야구) / 17281번 (Java)
1. 문제 접근
각 선수가 각 이닝에서 어떤 결과를 얻는지 미리 알고 있으며, 가장 많은 득점을 하는 타순을 찾고, 그 때의 득점을 구하면 된다.
총 9명의 선수가 존재하고, 이 때 1번 선수는 4번 타자로 고정이다.
4번 타자가 고정이므로 총 8명의 선수를 줄세우는 경우의 수(8! = 40,320)가 존재한다.
따라서 제한 시간 내에 실행할 수 있으므로, permutation(순열)을 이용해 모든 타자의 순번을 결정하면 된다.
모든 타순을 결정했다면, 이는 기저조건이 되고 모든 이닝을 순회하며 점수 계산을 진행하면 된다.
예시로 2루타의 경우 점수 계산 방법은 아래와 같다.
case ANTA_2:
// 3루에 주자가 있을 경우
if (currInning.base3) {
localScore++;
currInning.base3 = false;
}
// 2루에 주자가 있을 경우
if (currInning.base2) {
localScore++;
currInning.base2 = false;
}
// 1루에 주자가 있을 경우
if (currInning.base1) {
currInning.base3 = true;
currInning.base1 = false;
}
currInning.base2 = true;
break;
위와 같이 안타, 2루타, 3루타, 홈런에 대해 주자의 위치를 변경해주며, 득점이 발생할 경우, 더해주면 된다.
점수 계산 시, 각 베이스(1루, 2루, 3루)와 아웃 카운트를 저장할 클래스를 별도로 관리한다.
// 이닝 상태를 저장하는 클래스
static class GameState {
boolean base1, base2, base3;
int outCnt;
public GameState(boolean base1, boolean base2, boolean base3, int outCnt) {
this.base1 = base1;
this.base2 = base2;
this.base3 = base3;
this.outCnt = outCnt;
}
}
2. 코드
package boj;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
/**
* [BOJ] - ⚾(야구) / 17281번
* @author JinHxxxxKim
*
* 1. 이닝 수 N(2 ≤ N ≤ 50)를 입력받는다.
* 2. 각 선수가 각 이닝에서 얻는 결과가 1번 이닝부터 N번 이닝까지 입력받는다.
* 3. 1번 선수가 4번 타자는 확정이므로, 8명의 선수에 대해 순열을 구한다.
* 4. permutation
* - selectIdx = 9 (기저조건)
* - N개의 이닝에 대해 점수를 계산하고 더한다.
* - 최대 점수를 갱신한다.
*/
public class Sol_17281 {
private static final int ANTA_1 = 1;
private static final int ANTA_2 = 2;
private static final int ANTA_3 = 3;
private static final int HOMERUN = 4;
private static final int OUT = 0;
private static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
private static StringBuilder sb = new StringBuilder();
private static StringTokenizer st;
private static int inning;
private static int[][] playerResult;
private static int ans;
// 순열 관련 변수
private static int[] selected;
private static boolean[] isSelected;
public static void main(String[] args) throws Exception {
inning = Integer.parseInt(br.readLine().trim());
playerResult = new int[inning][9];
for (int inningCnt = 0; inningCnt < inning; ++inningCnt) {
st = new StringTokenizer(br.readLine().trim());
for (int playerCnt = 0; playerCnt < 9; ++playerCnt) {
playerResult[inningCnt][playerCnt] = Integer.parseInt(st.nextToken());
}
}
selected = new int[9];
isSelected = new boolean[9];
// 1번 선수는 4번 타자 고정
isSelected[0] = true;
permutation(0);
System.out.println(ans);
}
private static void permutation(int selectIdx) {
// 1번 선수는 4번 타자 고정
if (selectIdx == 3) {
selected[selectIdx] = 0;
permutation(selectIdx + 1);
}
// 기저조건
if (selectIdx == 9) {
// 점수 계산
int localScore = 0;
int currPlayerIdx = 0;
for (int inningCnt = 0; inningCnt < inning; ++inningCnt) {
GameState currInning = new GameState(false, false, false, 0);
// 3아웃이 되기 전까지 진행
while (currInning.outCnt < 3) {
int currplayer = selected[currPlayerIdx];
switch (playerResult[inningCnt][currplayer]) {
case ANTA_1:
// 3루에 주자가 있을 경우
if (currInning.base3) {
localScore++;
currInning.base3 = false;
}
// 2루에 주자가 있을 경우
if (currInning.base2) {
currInning.base3 = true;
currInning.base2 = false;
}
// 1루에 주자가 있을 경우
if (currInning.base1) {
currInning.base2 = true;
currInning.base1 = false;
}
currInning.base1 = true;
break;
case ANTA_2:
// 3루에 주자가 있을 경우
if (currInning.base3) {
localScore++;
currInning.base3 = false;
}
// 2루에 주자가 있을 경우
if (currInning.base2) {
localScore++;
currInning.base2 = false;
}
// 1루에 주자가 있을 경우
if (currInning.base1) {
currInning.base3 = true;
currInning.base1 = false;
}
currInning.base2 = true;
break;
case ANTA_3:
// 3루에 주자가 있을 경우
if (currInning.base3) {
localScore++;
currInning.base3 = false;
}
// 2루에 주자가 있을 경우
if (currInning.base2) {
localScore++;
currInning.base2 = false;
}
// 1루에 주자가 있을 경우
if (currInning.base1) {
localScore++;
currInning.base1 = false;
}
currInning.base3 = true;
break;
case HOMERUN:
if (currInning.base3) {
localScore++;
currInning.base3 = false;
}
// 2루에 주자가 있을 경우
if (currInning.base2) {
localScore++;
currInning.base2 = false;
}
// 1루에 주자가 있을 경우
if (currInning.base1) {
localScore++;
currInning.base1 = false;
}
localScore++;
break;
case OUT:
currInning.outCnt++;
break;
}
// 다음 타자로 교체
currPlayerIdx = (currPlayerIdx + 1) % 9;
}
}
ans = Math.max(ans, localScore);
return;
}
for (int elementIdx = 1; elementIdx < 9; ++elementIdx) {
if (isSelected[elementIdx])
continue;
// 전처리
isSelected[elementIdx] = true;
selected[selectIdx] = elementIdx;
// 재귀
permutation(selectIdx + 1);
// 후처리
isSelected[elementIdx] = false;
}
}
// 이닝 상태를 저장하는 클래스
static class GameState {
boolean base1, base2, base3;
int outCnt;
public GameState(boolean base1, boolean base2, boolean base3, int outCnt) {
super();
this.base1 = base1;
this.base2 = base2;
this.base3 = base3;
this.outCnt = outCnt;
}
}
}
3. 디버깅
선수의 명 수가 9명으로 고정 + 4번 타자 고정이라는 힌트를 이용해 순열을 이용해 구현하였으며, 이닝의 상태를 정확하게 관리하기 위해 별도의 `GameState`클래스를 선언해 사용했다.
또한, 각 상황(안타, 2루타, 3루타, 홈런)에 대해 정확하게 시뮬레이션 돌려보고 구현하는 것이 중요한 것 같다.
4. 결과
'알고리즘' 카테고리의 다른 글
[BOJ] - 회전 초밥 / 15961 (Java) (0) | 2024.02.20 |
---|---|
[BOJ] - 치킨 배달 / 15686번 (Java) (0) | 2024.02.20 |
[BOJ] - 적록색약 / 10026번 (Java) (0) | 2024.02.20 |
[BOJ] - 치즈 / 2636 (Java) (0) | 2024.02.20 |
[BOJ] - 줄세우기 / 2252번 (Java) (0) | 2024.02.20 |