본문 바로가기
알고리즘/BOJ(백준)

[ 백준-17281번 / 완전탐색 ] ⚾ (삼성SW)

by 뎁꼼 2020. 4. 20.

1. 문제


 

 

17281번: ⚾

⚾는 9명으로 이루어진 두 팀이 공격과 수비를 번갈아 하는 게임이다. 하나의 이닝은 공격과 수비로 이루어져 있고, 총 N이닝동안 게임을 진행해야 한다. 한 이닝에 3아웃이 발생하면 이닝이 종료되고, 두 팀이 공격과 수비를 서로 바꾼다. 두 팀은 경기가 시작하기 전까지 타순(타자가 타석에 서는 순서)을 정해야 하고, 경기 중에는 타순을 변경할 수 없다. 9번 타자까지 공을 쳤는데 3아웃이 발생하지 않은 상태면 이닝은 끝나지 않고, 1번 타자가 다시 타석에

www.acmicpc.net

문제

⚾는 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번 타자로 미리 결정했다. 이제 다른 선수의 타순을 모두 결정해야 한다. 아인타는 각 선수가 각 이닝에서 어떤 결과를 얻는지 미리 알고 있다. 가장 많은 득점을 하는 타순을 찾고, 그 때의 득점을 구해보자.

입력

첫째 줄에 이닝 수 N(2 ≤ N ≤ 50)이 주어진다. 둘째 줄부터 N개의 줄에는 각 선수가 각 이닝에서 얻는 결과가 1번 이닝부터 N번 이닝까지 순서대로 주어진다. 이닝에서 얻는 결과는 9개의 정수가 공백으로 구분되어져 있다. 각 결과가 의미하는 정수는 다음과 같다.

  • 안타: 1
  • 2루타: 2
  • 3루타: 3
  • 홈런: 4
  • 아웃: 0

각 이닝에는 아웃을 기록하는 타자가 적어도 한 명 존재한다.

출력

아인타팀이 얻을 수 있는 최대 점수를 출력한다.

예제 입력 1 복사

2 4 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0

예제 출력 1 복사

1

예제 입력 2 복사

2 4 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0

예제 출력 2 복사

4

예제 입력 3 복사

2 0 4 4 4 4 4 4 4 4 0 4 4 4 4 4 4 4 4

예제 출력 3 복사

43

 

 

2. 소스코드


1회차

 

- 총 경우의 수는 9!으로 362,880‬.

- 1~9번 선수가 있지만, 1번선수는 무조건 4번타자인 조건이 존재. 정말 단순히 생각해서 1~9 선수를 vector에 담고 next_permutation을 이용, 1번 선수가 4번 타석에 있지 않는 경우는 pass해서 문제를 풀었음.

- 중간에 백트래킹 없이 무조건 모든 순서를 만들고 비교하니, dfs나 bfs에 비해 확실히 속도가 느림. 

 

- 반복이 많다보니 조건문의 사소한 차이에도 시간차가 많이 발생했다. 

 

1) 하드코딩 vs 반복문 (홈런 처리 조건)

- 왼쪽, 오른쪽 코드는 100% 같은 기능이나, 왼쪽으로 구현한 쪽이 20ms 더 빨랐다.

- 조건문이 좀 더 간결하지만, 하드코딩이 더 빠른 경우.

               if (curHit == HOMERUN) {

                        res++;

                        if (base[0] == 1) {

                            base[0] = 0; res++;

                        }

                        if (base[1] == 1) {

                            base[1] = 0; res++;

                        }

                        if (base[2] == 1) {

                            base[2] = 0; res++;

                        }

                    }

             if (curHit == HOMERUN) {

                        res++;

                        for (int k = 0; k < 3; ++k) {

                            if (base[k] == 1) { 

                                base[k] = 0; res++; 

                            }

                        }

                    }

- 어셈블리어

더보기
 if (curHit == HOMERUN) { 
00320868  cmp         dword ptr [ebp-5Ch],4   
0032086C  jne         findMaxPoint+166h (03208F6h)   
                        res++; 
00320872  mov         eax,dword ptr [ebp-18h]   
00320875  add         eax,1   
00320878  mov         dword ptr [ebp-18h],eax   
                        if (base[0] == 1) { 
0032087B  mov         eax,4   
00320880  imul        ecx,eax,0   
00320883  cmp         dword ptr [ebp+ecx-44h],1   
00320888  jne         findMaxPoint+113h (03208A3h)   
                            base[0] = 0; res++; 
0032088A  mov         eax,4   
0032088F  imul        ecx,eax,0   
00320892  mov         dword ptr [ebp+ecx-44h],0   
0032089A  mov         eax,dword ptr [ebp-18h]   
0032089D  add         eax,1   
003208A0  mov         dword ptr [ebp-18h],eax   
                        } 
                        if (base[1] == 1) { 
003208A3  mov         eax,4   
003208A8  shl         eax,0   
003208AB  cmp         dword ptr [ebp+eax-44h],1   
003208B0  jne         findMaxPoint+13Bh (03208CBh)   
                            base[1] = 0; res++; 
003208B2  mov         eax,4   
003208B7  shl         eax,0   
003208BA  mov         dword ptr [ebp+eax-44h],0   
003208C2  mov         eax,dword ptr [ebp-18h]   
003208C5  add         eax,1   
003208C8  mov         dword ptr [ebp-18h],eax   
                        } 
                        if (base[2] == 1) { 
003208CB  mov         eax,4   
003208D0  shl         eax,1   
003208D2  cmp         dword ptr [ebp+eax-44h],1   
003208D7  jne         findMaxPoint+161h (03208F1h)   
                            base[2] = 0; res++; 
003208D9  mov         eax,4   
003208DE  shl         eax,1   
003208E0  mov         dword ptr [ebp+eax-44h],0   
003208E8  mov         eax,dword ptr [ebp-18h]   
003208EB  add         eax,1   
003208EE  mov         dword ptr [ebp-18h],eax   
                        } 
                    } 
003208F1  jmp         findMaxPoint+237h (03209C7h)  
if (curHit == HOMERUN) { 
00E60868  cmp         dword ptr [ebp-5Ch],4   
00E6086C  jne         findMaxPoint+124h (0E608B4h)   
                        res++; 
00E6086E  mov         eax,dword ptr [ebp-18h]   
00E60871  add         eax,1   
00E60874  mov         dword ptr [ebp-18h],eax   
                        for (int k = 0; k < 3; ++k) { 
00E60877  mov         dword ptr [ebp-68h],0   
00E6087E  jmp         findMaxPoint+0F9h (0E60889h)   
00E60880  mov         eax,dword ptr [ebp-68h]   
00E60883  add         eax,1   
00E60886  mov         dword ptr [ebp-68h],eax   
00E60889  cmp         dword ptr [ebp-68h],3   
00E6088D  jge         findMaxPoint+11Fh (0E608AFh)   
                            if (base[k] == 1) { 
00E6088F  mov         eax,dword ptr [ebp-68h]   
00E60892  cmp         dword ptr [ebp+eax*4-44h],1   
00E60897  jne         findMaxPoint+11Dh (0E608ADh)   
                                base[k] = 0; res++; 
00E60899  mov         eax,dword ptr [ebp-68h]   
00E6089C  mov         dword ptr [ebp+eax*4-44h],0   
00E608A4  mov         eax,dword ptr [ebp-18h]   
00E608A7  add         eax,1   
00E608AA  mov         dword ptr [ebp-18h],eax   
                            } 
                        } 
00E608AD  jmp         findMaxPoint+0F0h (0E60880h)   
                    }

 

2) while문 vs for문 (안타 처리 조건)

- 왼쪽, 오른쪽 코드는 100% 같은 기능이나, 왼쪽으로 구현한 쪽이 70ms 더 빨랐다.

- while 문에서 변수를 하나더 적게쓰고, 초기화, 대입을 하지않아 차이가 발생.

- while문이 무조건 for문보다 빠르다는 것은 아니다.

- 어셈블리어에서도 확인 가능하다.

- 정말 작은 차이지만 선수 순서마다, 이닝마다, 선수가 안타를 칠때마다 계속 반복되므로 큰 차이가 발생.

              while(curHit--) {

              for (int k = 2; k >= 0; --k) {

                  if (k == 2 and base[k] == 1) {

                      res++; base[k] = 0;

                   }

                  else if (k != 2 and base[k] == 1) {

                        base[k + 1]++; base[k] = 0;

                   }

                 }

                }

         base[score[i][seq[curBatter]] - 1]++;

            }            

         for(int j = 0; j < curHit ; ++j) { 

              for (int k = 2; k >= 0; --k) { 

                if (k == 2 and base[k] == 1) { 

                      res++; base[k] = 0; 

                 } 

                else if (k != 2 and base[k] == 1) { 

                       base[k + 1]++; base[k] = 0; 

                                } 

                            } 

                        } 

   base[score[i][seq[curBatter]] - 1]++; 

   }           

- 어셈블리어

더보기
while(curHit--) { 
010308B4  mov         eax,dword ptr [ebp-5Ch]   
010308B7  mov         dword ptr [ebp-154h],eax   
010308BD  mov         ecx,dword ptr [ebp-5Ch]   
010308C0  sub         ecx,1   
010308C3  mov         dword ptr [ebp-5Ch],ecx   
010308C6  cmp         dword ptr [ebp-154h],0   
010308CD  je          findMaxPoint+1B0h (01030940h)   
                            for (int k = 2; k >= 0; --k) { 
010308CF  mov         dword ptr [ebp-74h],2   
010308D6  jmp         findMaxPoint+151h (010308E1h)   
010308D8  mov         eax,dword ptr [ebp-74h]   
010308DB  sub         eax,1   
010308DE  mov         dword ptr [ebp-74h],eax   
010308E1  cmp         dword ptr [ebp-74h],0   
010308E5  jl          findMaxPoint+1ABh (0103093Bh)   
                                if (k == 2 and base[k] == 1) { 
010308E7  cmp         dword ptr [ebp-74h],2   
010308EB  jne         findMaxPoint+17Dh (0103090Dh)   
010308ED  mov         eax,dword ptr [ebp-74h]   
010308F0  cmp         dword ptr [ebp+eax*4-44h],1   
010308F5  jne         findMaxPoint+17Dh (0103090Dh)   
                                    res++; base[k] = 0; 
010308F7  mov         eax,dword ptr [ebp-18h]   
010308FA  add         eax,1   
010308FD  mov         dword ptr [ebp-18h],eax   
                                    res++; base[k] = 0; 
01030900  mov         eax,dword ptr [ebp-74h]   
01030903  mov         dword ptr [ebp+eax*4-44h],0   
                                } 
0103090B  jmp         findMaxPoint+1A9h (01030939h)   
                                else if(k != 2 and base[k] == 1) { 
0103090D  cmp         dword ptr [ebp-74h],2   
01030911  je          findMaxPoint+1A9h (01030939h)   
01030913  mov         eax,dword ptr [ebp-74h]   
01030916  cmp         dword ptr [ebp+eax*4-44h],1   
0103091B  jne         findMaxPoint+1A9h (01030939h)   
                                    base[k + 1]++; base[k] = 0; 
0103091D  mov         eax,dword ptr [ebp-74h]   
01030920  mov         ecx,dword ptr [ebp+eax*4-40h]   
01030924  add         ecx,1   
01030927  mov         edx,dword ptr [ebp-74h]   
0103092A  mov         dword ptr [ebp+edx*4-40h],ecx   
0103092E  mov         eax,dword ptr [ebp-74h]   
01030931  mov         dword ptr [ebp+eax*4-44h],0   
                                } 
                            } 
01030939  jmp         findMaxPoint+148h (010308D8h)   
                        } 
0103093B  jmp         findMaxPoint+124h (010308B4h)
for (int j = 0; j < curHit; ++j) { 
00FC08B4  mov         dword ptr [ebp-74h],0   
00FC08BB  jmp         findMaxPoint+136h (0FC08C6h)   
00FC08BD  mov         eax,dword ptr [ebp-74h]   
00FC08C0  add         eax,1   
00FC08C3  mov         dword ptr [ebp-74h],eax   
00FC08C6  mov         eax,dword ptr [ebp-74h]   
00FC08C9  cmp         eax,dword ptr [ebp-5Ch]   
00FC08CC  jge         findMaxPoint+1ACh (0FC093Ch)   
                            for (int k = 2; k >= 0; --k) { 
00FC08CE  mov         dword ptr [ebp-80h],2   
00FC08D5  jmp         findMaxPoint+150h (0FC08E0h)   
00FC08D7  mov         eax,dword ptr [ebp-80h]   
00FC08DA  sub         eax,1   
00FC08DD  mov         dword ptr [ebp-80h],eax   
00FC08E0  cmp         dword ptr [ebp-80h],0   
00FC08E4  jl          findMaxPoint+1AAh (0FC093Ah)   
                                if (k == 2 and base[k] == 1) { 
00FC08E6  cmp         dword ptr [ebp-80h],2   
00FC08EA  jne         findMaxPoint+17Ch (0FC090Ch)   
00FC08EC  mov         eax,dword ptr [ebp-80h]   
00FC08EF  cmp         dword ptr [ebp+eax*4-44h],1   
00FC08F4  jne         findMaxPoint+17Ch (0FC090Ch)   
                                    res++; base[k] = 0; 
00FC08F6  mov         eax,dword ptr [ebp-18h]   
00FC08F9  add         eax,1   
                                    res++; base[k] = 0; 
00FC08FC  mov         dword ptr [ebp-18h],eax   
00FC08FF  mov         eax,dword ptr [ebp-80h]   
00FC0902  mov         dword ptr [ebp+eax*4-44h],0   
                                } 
00FC090A  jmp         findMaxPoint+1A8h (0FC0938h)   
                                else if(k != 2 and base[k] == 1) { 
00FC090C  cmp         dword ptr [ebp-80h],2   
00FC0910  je          findMaxPoint+1A8h (0FC0938h)   
00FC0912  mov         eax,dword ptr [ebp-80h]   
00FC0915  cmp         dword ptr [ebp+eax*4-44h],1   
00FC091A  jne         findMaxPoint+1A8h (0FC0938h)   
                                    base[k + 1]++; base[k] = 0; 
00FC091C  mov         eax,dword ptr [ebp-80h]   
00FC091F  mov         ecx,dword ptr [ebp+eax*4-40h]   
00FC0923  add         ecx,1   
00FC0926  mov         edx,dword ptr [ebp-80h]   
00FC0929  mov         dword ptr [ebp+edx*4-40h],ecx   
00FC092D  mov         eax,dword ptr [ebp-80h]   
00FC0930  mov         dword ptr [ebp+eax*4-44h],0   
                                } 
                            } 
00FC0938  jmp         findMaxPoint+147h (0FC08D7h)   
                        } 
00FC093A  jmp         findMaxPoint+12Dh (0FC08BDh)  

 

#pragma warning (disable : 4996)

#define STRIKE 0
#define HOMERUN 4

#include <cstring>
#include <stdio.h>
#include <algorithm>
#include <vector>
#include <tuple>

using namespace std;

int n, ans;
vector<vector<int>> score;
vector<int> seq = { 0,1,2,3,4,5,6,7,8 };

void findMaxPoint() {

    do
    {   //0번선수가 3번타자가 아닌경우 PASS
        if (seq[3] != 0) continue;
        
        //전체 이닝 진행
        int nextBatter = 0, res = 0;
        for (int i = 0; i < n; ++i) {
            int out = 0, base[3] = { 0, };
            //한 이닝 진행
            int curBatter = nextBatter;
            while (out <  3)
            {   
                int curHit = score[i][seq[curBatter]];
                if (curHit == STRIKE) {
                    out++;  
                }
                else {
                    if (curHit == HOMERUN) {
                        res++;
                        if (base[0] == 1) {
                            base[0] = 0; res++;
                        }
                        if (base[1] == 1) {
                            base[1] = 0; res++;
                        }
                        if (base[2] == 1) {
                            base[2] = 0; res++;
                        }
                    }
                    else { //안타 인 경우
                       
                        while(curHit--) {
                            for (int k = 2; k >= 0; --k) {
                                if (k == 2 and base[k] == 1) {
                                    res++; base[k] = 0;
                                }
                                else if (k != 2 and base[k] == 1) {
                                    base[k + 1]++; base[k] = 0;
                                }
                            }
                        }
                        base[score[i][seq[curBatter]] - 1]++;
                    }            
                }
                curBatter = (curBatter + 1) % 9; // 타자 순환
            }
            nextBatter = curBatter; // 3아웃으로 끝. 다음 타자 저장.
        }
        if (ans < res) ans = res;

    } while (next_permutation(seq.begin(),seq.end()));

}

int main()
{
    scanf("%d",&n);
    for (int i = 0; i < n; ++i) {
        vector<int> eachEning;
        for (int j = 0; j < 9; ++j) {
            int temp;
            scanf("%d", &temp);
            eachEning.push_back(temp);
        }
        score.push_back(eachEning);
    }
 
    findMaxPoint();
 
    printf("%d", ans);
    return 0;
}
 

 

 

2회차

 

- dfs를 이용함. 의외로 dfs 자체만으로는 next_permutation로 해결했을때와 유의미한 차이가 나지 않았다.

- 오히려 타자의 베이스 위치 계산 알고리즘 개선이 효과가 컸다.  bit와 shift연산을 이용함.

- 1회차 330ms -> 2회차 180ms.

- 배열을 순회할때 나머지 연산을 이용하면 코드가 간결하지만, 나머지 연산을 항상 수행해야하므로 많은 반복이 있는 경우엔 속도가 꽤 많이 차이가 났다. 약 40ms.

		//curBatter = (curBatter + 1) % 9; //나머지 연산을 항상해야 하므로 속도가 느리다.
                if (curBatter == 8) curBatter = 0;
                else ++curBatter;

 

 

소스코드

#define STRIKE 0

#include <iostream>
#include <vector>
#include <bitset>

using namespace std;

int n, ans;
vector<vector<int>> score;

int seq[9] = { -1,-1,-1,0,-1,-1,-1,-1,-1 };

void findMaxPoint(int num) {

    if (num == 9) { // 순서를 모두 뽑은 경우 진행
        int curBatter = 0, res = 0;
        for (int i = 0; i < n; ++i) {//전체 이닝 진행
            int out = 0;
            bitset <8> runs;
            //한 이닝 진행
            while (out < 3)
            {
                int curHit = score[i][seq[curBatter]];
                if (curHit == STRIKE) {
                    ++out;
                }
                else {
                    runs[0] = 1;
                    runs <<= curHit;
                    res += runs[4] + runs[5] + runs[6] + runs[7];
                    runs[4] = 0; runs[5] = 0; runs[6] = 0; runs[7] = 0;
                }
                //curBatter = (curBatter + 1) % 9; //나머지 연산을 항상해야 하므로 속도가 느리다.
                if (curBatter == 8) curBatter = 0;
                else ++curBatter;
            }
            //한 이닝 끝
        }
        ans = ans < res ? res : ans;
        return;
    }
    for (int i = 0; i < 9; ++i) {

        if (seq[i] == -1) {
            seq[i] = num;
            findMaxPoint(num + 1);
            seq[i] = -1;
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    cin >> n;
    for (int i = 0; i < n; ++i) {
        vector<int> eachEning;
        for (int j = 0; j < 9; ++j) {
            int temp;
            cin >> temp;
            eachEning.push_back(temp);
        }
        score.push_back(eachEning);
    }

    findMaxPoint(1);

    printf("%d", ans);
    return 0;
}