궤도

[백준] 17281번 : ⚾ 본문

💻 현생/⛓ 알고리즘

[백준] 17281번 : ⚾

영이오 2021. 7. 4. 16:20

문제

 


풀이

 

각 선수가 몇 번 타자가 될 지는 next_permutation으로 구하면 된다.

다만, 1번 선수가 4번 타자인건 고정이니 2~9번 선수들에 대해서만 순열을 돌리고 4번 타자가 된 선수와 1번 선수를 바꾸면 되겠다.

 

점수나 다음 타자는 이닝이 바뀌어도 누적되는 결과니까 전역변수로 처리하면 된다.

그리고 1, 2, 3루 각 플레이트에 선수가 있는지 없는지를 저장해야 하는데...나는 비트 마스킹을 썼다.

 

1110 : 1, 2, 3루에 주자 존재

0100 : 2루에 주자 존재

1010 : 1, 3루에 주자 존재

 

가장 오른쪽 비트는 이번에 나선 타자를 위해 남겨뒀다.

아무튼 이런식으로 하면 주자 이동도 3루타 일때 (1011)<<3 = 1000 이런식으로 간단하게 처리할 수 있다.

엄밀히 말하면 1011000이 되겠지만 그렇게 되지 않도록 내가 처리할 것이다.


소스코드

 

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int score, player;
vector<int> entry = {0, 1, 2, 3, 4, 5, 6, 7, 8}; //선수 순서
vector<vector<int>> board;

void running(int &run, int num) {
    run |= (1 << 0); //0번 플레이트에 선수 표시
    for (int i = (4 - num); i < 4; i++) {
        if (run & (1 << i)) { //홈에 도착하게 될 주자
            run &= ~(1 << i); //0으로 변경
            score++; //점수 증가
        }
    }
    run = run << num; //주자 이동
}

void playGame(int idx) {
    int out = 0, run = 0; //아웃카운트, 진루 상태
    while (out != 3) { //3아웃 미만일 동안
        int hit = board[idx][entry[player]]; //이번 주자의 결과
        if (!hit) //아웃
            out++;
        else //존재하는 모든 주자 진루
            running(run, hit); 
        player = (player + 1) % 9; //다음 선수
    }
}

int main() {
    int N, result = 0;

    cin >> N;
    board.assign(N, vector<int>(9));
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < 9; j++)
            cin >> board[i][j];
    }
    do {
        swap(entry[0], entry[3]); //1번 선수 4번 타자 되도록
        score = player = 0; //초기화
        for (int i = 0; i < N; i++) //모든 이닝 플레이
            playGame(i);
        result = max(result, score); //최댓값 갱신
        swap(entry[3], entry[0]); //원래대로 복구
    } while (next_permutation(entry.begin() + 1, entry.end()));
    cout << result;
}

전체 소스코드다.

 

    do {
        swap(entry[0], entry[3]); //1번 선수 4번 타자 되도록
        score = player = 0; //초기화
        for (int i = 0; i < N; i++) //모든 이닝 플레이
            playGame(i);
        result = max(result, score); //최댓값 갱신
        swap(entry[3], entry[0]); //원래대로 복구
    } while (next_permutation(entry.begin() + 1, entry.end()));

가능한 모든 엔트리에 대해 시뮬레이션을 돌려본다.

 

        score = player = 0; //초기화
        for (int i = 0; i < N; i++) //모든 이닝 플레이
            playGame(i);

각 엔트리마다 score와 player를 0으로 초기화 하고, 모든 이닝을 플레이해본다.

 

void playGame(int idx) {
    int out = 0, run = 0; //아웃카운트, 진루 상태
    while (out != 3) { //3아웃 미만일 동안
        int hit = board[idx][entry[player]]; //이번 주자의 결과
        if (!hit) //아웃
            out++;
        else //존재하는 모든 주자 진루
            running(run, hit);
        player = (player + 1) % 9; //다음 선수
    }
}

각 이닝은 아웃카운트가 3이 되면 끝난다.

 

        int hit = board[idx][entry[player]]; //이번 주자의 결과

이번 타자의 결과를 hit에 저장한다. 0~4까지의 수가 저장된다.

 

        if (!hit) //아웃
            out++;
        else //존재하는 모든 주자 진루
            running(run, hit);

0이면 아웃카운트를 늘려주고, 아니라면 진루 처리 한다.

 

void running(int &run, int num) {
    run |= (1 << 0); //0번 플레이트에 선수 표시
    for (int i = (4 - num); i < 4; i++) {
        if (run & (1 << i)) { //홈에 도착하게 될 주자
            run &= ~(1 << i); //0으로 변경
            score++; //점수 증가
        }
    }
    run = run << num; //주자 이동
}

먼저 run |= (1<<0)으로 타자의 진루를 표시한다.

 

    for (int i = (4 - num); i < 4; i++) {
        if (run & (1 << i)) { //홈에 도착하게 될 주자
            run &= ~(1 << i); //0으로 변경
            score++; //점수 증가
        }
    }

홈에 도착하게 될 주자가 몇명인지 세본다.

아까 1101에서 3루타를 쳐서 110 1000이 됐을때, 삐져나온 110에 대해 1의 개수를 세는 것이다.

개수를 세면서 0으로 변경해서 4비트를 유지한다.

 

    run = run << num; //주자 이동

그럼 주자 이동은 한줄로 처리할 수 있다.

 

        player = (player + 1) % 9; //다음 선수

while문을 돌면서 player의 인덱스를 하나씩 옮겨주는데 9번 타자 -> 1번 타자일 수도 있으니 모듈러로 처리한다.

 

        result = max(result, score); //최댓값 갱신

이렇게 최댓값 갱신하고 출력하면 된다.

Comments