알고리즘

[백준] 승부예측 (파이썬)

유병각 2022. 2. 4. 21:48

문제링크

https://www.acmicpc.net/problem/15997

 

15997번: 승부 예측

첫 번째 줄에 조별리그를 진행할 국가명 네 개가 공백으로 구분되어 주어진다. 주어지는 모든 국가명은 알파벳 대문자로만 구성된 길이가 1 이상 10 이하인 문자열이다. 두 번째 줄부터 일곱 번

www.acmicpc.net

 

문제풀이

이 문제는 해결 방법을 떠올리기도 어렵고, 구현하는게 복잡한 문제이다.

총 4개의 팀이 6번의 매치를 하는데 각 매치마다 이기거나, 지거나, 비기는 모든 경우의 확률을

완전탐색한 뒤 1등과 2등을 뽑아 1등과 2등에게 각각의 확률을 더해주는 식으로 문제를 풀면된다.

 

이 문제를 구현하기 까다로웠는데 그 이유는,

1. 재귀함수와 for 문, 스택을 이용한 완전탐색 구현 

2. 데이터 파싱

3. 공동 1,2 등이 나올 각각의 케이스 분류

였다.

 

모든 매치는 총 3가지 경우의 수가 존재한다.( Win, Lose, Draw )

chance 라는 배열은 각 매치의 결과를 담은 배열이다.

만약 chance 의 길이가 6 일경우, 모든 매치가 종료된 것이므로 이때 1,2 등을 구해 확률을 더해주면 된다.

또한 for 문 밖에 chance.pop() 을 만들어줘서 재귀가 정상적으로 돌아가도록 만들어주었다.

 

시간복잡도

기본적으로 3^6 의 모든 경우의 수를 탐색하는데, 

각 경우마다 k번 의 예외처리를 해주어야 하므로,

TC = 3^6 * k 즉, Big(O) = (3^n) 이다.

 

소스코드

from collections import defaultdict;

teams = list(input().split());
chances = [];
ans = defaultdict(list);

for team in teams:
    ans[team] = 0;

for i in range(6):
    temp = list(input().split());
    chances.append(temp);

def calChance(chance, ans):
    mm = 1.00000000;
    d = defaultdict(int);

    for c in chance:
        teamA = c[0];
        scoreA = int(c[1]);
        teamB = c[2];
        scoreB = int(c[3]);
        m = float(c[4]);
        mm *= m;
        d[teamA] += scoreA;
        d[teamB] += scoreB;
    d = dict(sorted(d.items(), key=lambda item : -item[1]));
    
    temp = [];
    for key in d.keys():
        temp.append([key, d[key]]);
    
    # 2 등이 3등보다 승점이 높은경우
    if (temp[1][1] > temp[2][1]):
        ans[temp[0][0]] += mm;
        ans[temp[1][0]] += mm;
    
    # 2 등이 3등과 점수가 같을 경우
    else:
        # 3,4 등도 2등과 점수가 같은경우
        if (temp[2][1] == temp[3][1]):
            # 1,2 등도 점수가 같은경우
            if (temp[0][1] == temp[1][1]):
                for t in temp:
                    ans[t[0]] += (mm/2);
            # 1등과 2등이 점수가 같지 않은경우
            elif (temp[0][1] != temp[1][1]):
                ans[temp[0][0]] += mm;
                ans[temp[1][0]] += mm/3;
                ans[temp[2][0]] += mm/3;
                ans[temp[3][0]] += mm/3;
        # 3등과 4등이 점수가 같지 않은 경우
        elif (temp[2][1] != temp[3][1]):
            # 1 등과 2 등이 점수가 같은 경우
            if (temp[0][1] == temp[1][1]):
                ans[temp[0][0]] += (mm*2)/3;
                ans[temp[1][0]] += (mm*2)/3;
                ans[temp[2][0]] += (mm*2)/3;
           # 1등과 2등이 점수가 같지 않은경우
            elif (temp[0][1] != temp[1][1]):
                ans[temp[0][0]] += mm;
                ans[temp[1][0]] += mm/2;
                ans[temp[2][0]] += mm/2;

def dfs(level, idx, chance, ans):
    global chances;
    nowTeams = chances[level - 1];
    if idx == 0:
        temp = [nowTeams[0], 3,nowTeams[1], 0, nowTeams[idx + 2]];
    elif idx == 1:
        temp = [nowTeams[0], 1,nowTeams[1], 1, nowTeams[idx + 2]];
    elif idx == 2:
        temp = [nowTeams[0], 0,nowTeams[1], 3, nowTeams[idx + 2]];
    chance.append(temp);
    
    if (level == 6):
        calChance(chance, ans);
        chance.pop();
        return ;
    
    for i in range(3):
        if float(chances[level][i+2]) != 0:
            dfs(level + 1, i, chance, ans);
    chance.pop();


for i in range(3):
    if float(chances[0][i + 2]) != 0:
        dfs(1, i, [], ans);

for t in teams:
    print(ans[t]);