반응형

파이썬 / BOJ 백준 / 14889 스타트와 링크 - 백트래킹

 

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

 

14889번: 스타트와 링크

예제 2의 경우에 (1, 3, 6), (2, 4, 5)로 팀을 나누면 되고, 예제 3의 경우에는 (1, 2, 4, 5), (3, 6, 7, 8)로 팀을 나누면 된다.

www.acmicpc.net

 

문제

오늘은 스타트링크에 다니는 사람들이 모여서 축구를 해보려고 한다. 축구는 평일 오후에 하고 의무 참석도 아니다. 축구를 하기 위해 모인 사람은 총 N명이고 신기하게도 N은 짝수이다. 이제 N/2명으로 이루어진 스타트 팀과 링크 팀으로 사람들을 나눠야 한다.
BOJ
를 운영하는 회사 답게 사람에게 번호를 1부터 N까지로 배정했고, 아래와 같은 능력치를 조사했다. 능력치 Siji번 사람과 j번 사람이 같은 팀에 속했을 때, 팀에 더해지는 능력치이다. 팀의 능력치는 팀에 속한 모든 쌍의 능력치 Sij의 합이다. SijSji와 다를 수도 있으며, i번 사람과 j번 사람이 같은 팀에 속했을 때, 팀에 더해지는 능력치는 SijSji이다.
N=4
이고, S가 아래와 같은 경우를 살펴보자.

예를 들어, 1, 2번이 스타트, 3, 4번이 링크 팀에 속한 경우에 두 팀의 능력치는 아래와 같다.

스타트: S12 + S21 = 1 + 4 = 5

링크 팀: S34 + S43 = 2 + 5 = 7

1, 3번이 스타트, 2, 4번이 링크 팀에 속하면, 두 팀의 능력치는 아래와 같다.

스타트: S13 + S31 = 2 + 7 = 9

링크 팀: S24 + S42 = 6 + 4 = 10

축구를 재미있게 하기 위해서 스타트 팀의 능력치와 링크 팀의 능력치의 차이를 최소로 하려고 한다. 위의 예제와 같은 경우에는 1, 4번이 스타트, 2, 3번 팀이 링크 팀에 속하면 스타트 팀의 능력치는 6, 링크 팀의 능력치는 6이 되어서 차이가 0이 되고 이 값이 최소이다.

 

 

풀이

1. 조합으로 모든 팀 조합을 구해준 뒤, 각각의 팀 능력치를 생성해 비교합니다.

2. 팀이 2개이기 때문에, 하나의 팀을 combinations 함수를 이용해 N//2의 경우의 수를 구합니다.

3. 나머지 팀은 하나의 팀의 여집합으로 쉽게 구할 수 있습니다.

4. Combinations 함수를 모든 조합의 경우의 수를 출력해주는 함수입니다.

 

 

전체 코드

from itertools  import combinations

N = int(input())
S = [list(map(int, input().split())) for _ in range(N)]
member = [i for i in range(N)]

# 스타트팀의 N//2의 조합을 구함
team_combi = []
for team in combinations(member, N//2):
    team_combi.append(team)

min_result = 987654321
for k in range(len(team_combi)//2):

    # 스타트 팀의 여집합으로 링크 팀을 구함
    team_a = team_combi[k]
    team_b = list( set(member) - set(team_combi[k]))

    # 각 팀의 능력치의 합을 구함
    start_team = 0
    for i in range(len(team_a)):
        for j in team_a:
            start_team += S[team_a[i]][j]

    link_team = 0
    for i in range(len(team_b)):
        for j in team_b:
            link_team += S[team_b[i]][j]

    min_result = min(min_result, abs(start_team - link_team))

print(min_result)

 

반응형
반응형

파이썬 / BOJ 백준 / 14888  연산자 끼워넣기 - 백트래킹

 

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

 

14888번: 연산자 끼워넣기

첫째 줄에 수의 개수 N(2 ≤ N ≤ 11)가 주어진다. 둘째 줄에는 A1, A2, ..., AN이 주어진다. (1 ≤ Ai ≤ 100) 셋째 줄에는 합이 N-1인 4개의 정수가 주어지는데, 차례대로 덧셈(+)의 개수, 뺄셈(-)의 개수, 

www.acmicpc.net

 

문제

N개의 수로 이루어진 수열 A1, A2, ..., AN이 주어진다. , 수와 수 사이에 끼워넣을 수 있는 N-1개의 연산자가 주어진다. 연산자는 덧셈(+), 뺄셈(-), 곱셈(×), 나눗셈(÷)으로만 이루어져 있다.
우리는 수와 수 사이에 연산자를 하나씩 넣어서, 수식을 하나 만들 수 있다. 이때, 주어진 수의 순서를 바꾸면 안 된다.
예를 들어, 6개의 수로 이루어진 수열이 1, 2, 3, 4, 5, 6이고, 주어진 연산자가 덧셈(+) 2, 뺄셈(-) 1, 곱셈(×) 1, 나눗셈(÷) 1개인 경우에는 총 60가지의 식을 만들 수 있다. 예를 들어, 아래와 같은 식을 만들 수 있다.
1+2+3-4×5÷6
1÷2+3+4-5×6
1+2÷3×4-5+6
1÷2×3-4+5+6
식의 계산은 연산자 우선 순위를 무시하고 앞에서부터 진행해야 한다. , 나눗셈은 정수 나눗셈으로 몫만 취한다. 음수를 양수로 나눌 때는 C++14의 기준을 따른다. , 양수로 바꾼 뒤 몫을 취하고, 그 몫을 음수로 바꾼 것과 같다이에 따라서, 위의 식 4개의 결과를 계산해보면 아래와 같다.
1+2+3-4×5÷6 = 1
1÷2+3+4-5×6 = 12
1+2÷3×4-5+6 = 5
1÷2×3-4+5+6 = 7
N
개의 수와 N-1개의 연산자가 주어졌을 때, 만들 수 있는 식의 결과가 최대인 것과 최소인 것을 구하는 프로그램을 작성하시오.

 

풀이

1. 백트래킹을 이용하여 풀 수 있는 문제입니다.

2. 최대, 최소의 값을 위한 수식을 찾기 위해, 가지치기 없이 끝까지 탐색을 마쳐야 합니다.

3. idx == N이 되면, 현재값이 최소, 최대인지 검사하고, 재귀를 종료합니다.

 

전체 코드

import sys

N = int(input())
nums = list(map(int, input().split()))

a, b, c, d = map(int, input().split())
max_ans, min_ans = -sys.maxsize - 1, sys.maxsize

def solution(sum, idx, add, sub, mul, div):
    global max_ans, min_ans
    if idx == N:
        max_ans = max(max_ans, sum)
        min_ans = min(min_ans, sum)
        return

    if add > 0:
        solution(sum + nums[idx], idx + 1, add - 1, sub, mul, div)
    if sub > 0:
        solution(sum - nums[idx], idx + 1, add, sub - 1, mul, div)
    if mul > 0:
        solution(sum * nums[idx], idx + 1, add, sub, mul - 1, div)
    if div > 0:
        solution(int(sum / nums[idx]), idx + 1, add, sub, mul, div - 1)


solution(nums[0], 1, a, b, c, d)
print(max_ans)
print(min_ans)
반응형
반응형

파이썬 / BOJ 백준 / 2580 스도쿠 - 백트래킹

 

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

 

2580번: 스도쿠

스도쿠는 18세기 스위스 수학자가 만든 '라틴 사각형'이랑 퍼즐에서 유래한 것으로 현재 많은 인기를 누리고 있다. 이 게임은 아래 그림과 같이 가로, 세로 각각 9개씩 총 81개의 작은 칸으로 이루

www.acmicpc.net

문제

스도쿠는 18세기 스위스 수학자가 만든 '라틴 사각형'이랑 퍼즐에서 유래한 것으로 현재 많은 인기를 누리고 있다. 이 게임은 아래 그림과 같이 가로, 세로 각각 9개씩 총 81개의 작은 칸으로 이루어진 정사각형 판 위에서 이뤄지는데, 게임 시작 전 일부 칸에는 1부터 9까지의 숫자 중 하나가 쓰여 있다.

나머지 빈 칸을 채우는 방식은 다음과 같다.

각각의 가로줄과 세로줄에는 1부터 9까지의 숫자가 한 번씩만 나타나야 한다.

굵은 선으로 구분되어 있는 3x3 정사각형 안에도 1부터 9까지의 숫자가 한 번씩만 나타나야 한다.

위의 예의 경우, 첫째 줄에는 1을 제외한 나머지 2부터 9까지의 숫자들이 이미 나타나 있으므로 첫째 줄 빈칸에는 1이 들어가야 한다.

또한 위쪽 가운데 위치한 3x3 정사각형의 경우에는 3을 제외한 나머지 숫자들이 이미 쓰여있으므로 가운데 빈 칸에는 3이 들어가야 한다.

이와 같이 빈 칸을 차례로 채워 가면 다음과 같은 최종 결과를 얻을 수 있다.

게임 시작 전 스도쿠 판에 쓰여 있는 숫자들의 정보가 주어질 때 모든 빈 칸이 채워진 최종 모습을 출력하는 프로그램을 작성하시오.

 

풀이

1. 백트래킹을 이용하여 풀 수 있는 문제입니다.

2. 우선 0인 부분만 찾고, 그 부분을 하나씩 들어갈 숫자를 찾는, 재귀함수를 이용해 진행합니다.

3. 숫자를 찾을 때에는 행, , 3x3 박스 안에서 검사하여 찾습니다.

4. 검사하여 들어갈 숫자를 찾으면, 그 다음 0인 부분도 동일하게 찾습니다.

5. 들어갈 숫자가 하나도 없다면, 그 이전에 넣었던 숫자를 다른 숫자로 변경합니다.

 

전체 코드

board = [list(map(int, input().split())) for _ in range(9)]
zeros = [(i, j) for i in range(9) for j in range(9) if board[i][j] == 0]


def is_promising(i, j):
    promising = [1, 2, 3, 4, 5, 6, 7, 8, 9]

    # 행열 검사
    for k in range(9):
        if board[i][k] in promising:
            promising.remove(board[i][k])
        if board[k][j] in promising:
            promising.remove(board[k][j])
    i //= 3
    j //= 3
    for p in range(i * 3, (i + 1) * 3):
        for q in range(j * 3, (j + 1) * 3):
            if board[p][q] in promising:
                promising.remove(board[p][q])

    return promising


flag = False

def dfs(x):
    global flag

    if flag:  # 이미 답이 출력된 경우
        return

    if x == len(zeros):
        for row in board:
            print(*row)
        flag = True
        return

    else:
        (i, j) = zeros[x]
        promising = is_promising(i, j)
        for num in promising:
            board[i][j] = num
            dfs(x + 1)
            board[i][j] = 0
dfs(0)
반응형
반응형

파이썬 / BOJ 백준 / 15651번 N과 M(3) - 백트래킹

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

 

15651번: N과 M (3)

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해

www.acmicpc.net

문제

자연수 NM이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오.
- 1
부터 N까지 자연수 중에서 M개를 고른 수열
- 같은 수를 여러 번 골라도 된다
.

 

풀이

1. 백트래킹을 이용하여 풀 수 있는 문제입니다. 백트래킹의 가장 기초적인 문제라 할 수 있습니다. 15649문제에서 약간 변형된 문제입니다.

https://zidarn87.tistory.com/329

 

파이썬 / BOJ 백준 / 15649번 N과 M(1) - 백트래킹

파이썬 / BOJ 백준 / 15649번 N과 M(1) - 백트래킹 https://www.acmicpc.net/problem/15649 15649번: N과 M (1) 한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력..

zidarn87.tistory.com

2. 중복을 허용하기 떄문에, for문에서 중복을 확인하는 if문이 삭제되었습니다.

전체 코드

N, M = map(int, input().split())
lst = []

def dfs(cnt):
    if cnt - 1 == M:  # 탈출 조건
        print(' '.join(map(str, lst)))
        return

    for i in range(1, N+1):
        lst.append(i)
        dfs(cnt+1)
        lst.pop()
dfs(1)
반응형

+ Recent posts