문제

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

 

9095번: 1, 2, 3 더하기

각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 출력한다.

www.acmicpc.net

정수 4를 1, 2, 3의 합으로 나타내는 방법은 총 7가지가 있다. 합을 나타낼 때는 수를 1개 이상 사용해야 한다.

  • 1+1+1+1
  • 1+1+2
  • 1+2+1
  • 2+1+1
  • 2+2
  • 1+3
  • 3+1

정수 n이 주어졌을 때, n을 1, 2, 3의 합으로 나타내는 방법의 수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스는 한 줄로 이루어져 있고, 정수 n이 주어진다. n은 양수이며 11보다 작다.

출력

각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 출력한다.

 

코드

import sys
input = sys.stdin.readline

n = int(input())
for _ in range(n):
    k = int(input())
    if k == 1:
        print(1)
    elif k == 2:
        print(2)
    elif k == 3:
        print(4)
    else:
        answer = [0]*(k+1)
        answer[1] = 1
        answer[2] = 2
        answer[3] = 4
        for i in range(4, k+1):
            answer[i] = answer[i-1] + answer[i-2] + answer[i-3]
        print(answer[k])

 

n이 1, 2, 3일 때는 방법의 수를 간단히 구할 수 있고 이 값을 n이 4 이상인 경우부터 사용해야 하기 때문에 미리 배열에 저장해둔다.

n이 4 이상인 경우부터

(n-1의 경우의 수에 1을 더한 경우) + (n-2의 경우의 수에 2를 더한 경우) + (n-3의 경우의 수에 3을 더한 경우)

가 답이 된다.

예를 들어, n = 4 일때

1+1+1 / 1+2 / 2+1 / 3   ->    n = 3 인 경우의 끝에 1을 더한 경우

1 + 1 / 2   ->   n = 2 인 경우의 끝에 2를 더한 경우

1   ->   n = 1 인 경우우의 끝에 3을 더한 경우

들의 합인 7이 정답이 된다.

 

 

오랜만에 !!!! 나 혼자 뚝딱 풀고 제출했더니 맞았던 문제 !!!

저번 동적 프로그래밍 문제 풀면서, 동적 프로그래밍 생각이 안 나서 결국 복잡하게 구현하다가 시간초과가 났었다.

이번에는 문제 보자마자 동적 프로그래밍 생각이 났구, 그대로 풀어보니 정답이 나왔다.

동적 프로그래밍 문제 중에서 기본 중의 기본 문제지만 그래도 뿌듯뿌..ㅎㅎ

'Algorithm' 카테고리의 다른 글

[백준] 2606 바이러스 / python  (0) 2024.04.12
[백준] 2193 이친수 / python  (0) 2024.04.11
[백준] 1072 게임 / python  (0) 2024.04.10
[백준] 1735 분수 합  (0) 2024.04.08
[백준] 1003 피보나치 함수 / python  (1) 2024.04.07
 

1072번: 게임

김형택은 지금 몰래 Spider Solitaire(스파이더 카드놀이)를 하고 있다. 형택이는 이 게임을 이길 때도 있었지만, 질 때도 있었다. 누군가의 시선이 느껴진 형택이는 게임을 중단하고 코딩을 하기 시

www.acmicpc.net

 

김형택은 지금 몰래 Spider Solitaire(스파이더 카드놀이)를 하고 있다. 형택이는 이 게임을 이길 때도 있었지만, 질 때도 있었다. 누군가의 시선이 느껴진 형택이는 게임을 중단하고 코딩을 하기 시작했다. 의심을 피했다고 생각한 형택이는 다시 게임을 켰다. 그 때 형택이는 잠시 코딩을 하는 사이에 자신의 게임 실력이 눈에 띄게 향상된 것을 알았다.

이제 형택이는 앞으로의 모든 게임에서 지지 않는다. 하지만, 형택이는 게임 기록을 삭제 할 수 없기 때문에, 자신의 못하던 예전 기록이 현재 자신의 엄청난 실력을 증명하지 못한다고 생각했다.

게임 기록은 다음과 같이 생겼다.

  • 게임 횟수 : X
  • 이긴 게임 : Y (Z%)
  • Z는 형택이의 승률이고, 소수점은 버린다. 예를 들어, X=53, Y=47이라면, Z=88이다.

X와 Y가 주어졌을 때, 형택이가 게임을 최소 몇 번 더 해야 Z가 변하는지 구하는 프로그램을 작성하시오.

입력

각 줄에 정수 X와 Y가 주어진다.

출력

첫째 줄에 형택이가 게임을 최소 몇 판 더 해야하는지 출력한다. 만약 Z가 절대 변하지 않는다면 -1을 출력한다.

 

코드

import sys
input = sys.stdin.readline

x,y = map(int, input().split())
z = (y*100)//x

if z >= 99:
    print(-1)
else:
    answer = 0
    left = 0
    right = x

    while left <= right:
        mid = (left+right) // 2
        if (y+mid)*100 // (x+mid) <= z:
            left = mid+1
        else:
            answer = mid
            right = mid-1
    print(answer)

 

Z가 99 이상이라면 승률은 절대 변하지 않으므로 -1을 출력한다.

나는 처음에 순차적으로 탐색하는 방법으로 코드를 구현했는데 생각해보면 X는 최대 1,000,000,000까지 가능하므로 순차적으로 탐색하는 O(n)의 시간복잡도를 적용하면 시간초과가 날 수 밖에 없다.

 

1부터 X까지 오름차순인 상태에서 탐색 가능한 이진 탐색을 구현했다.

 

NEW

이진탐색(Binary Search)

데이터의 크기가 매우 클 때 선형 탐색 대신 쓸 수 있는 방법이 이진 탐색이다.

N개의 데이터가 있을 때 범위를 절반씩 줄여나가므로 O(logn)의 효율적인 시간복잡도를 가진다.

주의할 점은 정렬된 상태여야한다.

리스트의 전체 범위에서 시작해야 하므로 left, right 두 개의 변수가 필요하다.

보통 left는 시작을 뜻하는 0, right는 끝을 뜻하는 len(data)-1로 선언

left <= right인 동안 반복을 계속 돌려주면 된다.

 

 

int((Y/X) * 100)로 구현했는데 틀렸습니다 가 나왔다.

int((Y*100)/X) 나 (Y*100)//X는 정답으로 처리됐다.구글링해보니 python에서는 부동소수점 오차가 나서 정확하지 않다고 한다.

'Algorithm' 카테고리의 다른 글

[백준] 2193 이친수 / python  (0) 2024.04.11
[백준] 9095 : 1, 2, 3 더하기  (0) 2024.04.11
[백준] 1735 분수 합  (0) 2024.04.08
[백준] 1003 피보나치 함수 / python  (1) 2024.04.07
[백준] 1929 소수 구하기 / python  (2) 2024.04.07
 

1735번: 분수 합

첫째 줄과 둘째 줄에, 각 분수의 분자와 분모를 뜻하는 두 개의 자연수가 순서대로 주어진다. 입력되는 네 자연수는 모두 30,000 이하이다.

www.acmicpc.net

분수 A/B는 분자가 A, 분모가 B인 분수를 의미한다. A와 B는 모두 자연수라고 하자.

두 분수의 합 또한 분수로 표현할 수 있다. 두 분수가 주어졌을 때, 그 합을 기약분수의 형태로 구하는 프로그램을 작성하시오. 기약분수란 더 이상 약분되지 않는 분수를 의미한다.

입력

첫째 줄과 둘째 줄에, 각 분수의 분자와 분모를 뜻하는 두 개의 자연수가 순서대로 주어진다. 입력되는 네 자연수는 모두 30,000 이하이다.

출력

첫째 줄에 구하고자 하는 기약분수의 분자와 분모를 뜻하는 두 개의 자연수를 빈 칸을 사이에 두고 순서대로 출력한다.

 

코드

import sys
input = sys.stdin.readline

a,b = map(int,input().split())
c,d = map(int,input().split())

up = a*d + c*b
down = b*d

#최대공약수
def GCD(x,y):
    while y:
        x, y = y, x%y
    return x
gcd = GCD(up, down)

up = int(up/gcd)
down = int(down/gcd)
print(str(int(up))+" "+str(int(down)))

 

a / b + c / d = ( bc + ad) / bd

이 문제에서 핵심은 기약분수로 표현하는 것이다.

분자와 분모의 최대공약수로 각각 나눠주면 기약분수로 표현할 수 있다.

유클리드 알고리즘 ( Euclidean algorithm )

def gcd(a, b):
    while b != 0:
        a, b = b, a % b
    return a

 

a % b 는 a를 b로 나눈 나머지를 의미한다. 

b가 0이 되었을 때 a의 값이 a와 b의 최대공약수이다.

 

1003번: 피보나치 함수

각 테스트 케이스마다 0이 출력되는 횟수와 1이 출력되는 횟수를 공백으로 구분해서 출력한다.

www.acmicpc.net

다음 소스는 N번째 피보나치 수를 구하는 C++ 함수이다.

int fibonacci(int n) {
    if (n == 0) {
        printf("0");
        return 0;
    } else if (n == 1) {
        printf("1");
        return 1;
    } else {
        return fibonacci(n‐1) + fibonacci(n‐2);
    }
}

fibonacci(3)을 호출하면 다음과 같은 일이 일어난다.

  • fibonacci(3)은 fibonacci(2)와 fibonacci(1) (첫 번째 호출)을 호출한다.
  • fibonacci(2)는 fibonacci(1) (두 번째 호출)과 fibonacci(0)을 호출한다.
  • 두 번째 호출한 fibonacci(1)은 1을 출력하고 1을 리턴한다.
  • fibonacci(0)은 0을 출력하고, 0을 리턴한다.
  • fibonacci(2)는 fibonacci(1)과 fibonacci(0)의 결과를 얻고, 1을 리턴한다.
  • 첫 번째 호출한 fibonacci(1)은 1을 출력하고, 1을 리턴한다.
  • fibonacci(3)은 fibonacci(2)와 fibonacci(1)의 결과를 얻고, 2를 리턴한다.

1은 2번 출력되고, 0은 1번 출력된다. N이 주어졌을 때, fibonacci(N)을 호출했을 때, 0과 1이 각각 몇 번 출력되는지 구하는 프로그램을 작성하시오.

입력

첫째 줄에 테스트 케이스의 개수 T가 주어진다.

각 테스트 케이스는 한 줄로 이루어져 있고, N이 주어진다. N은 40보다 작거나 같은 자연수 또는 0이다.

출력

각 테스트 케이스마다 0이 출력되는 횟수와 1이 출력되는 횟수를 공백으로 구분해서 출력한다.

 

코드

import sys
input = sys.stdin.readline

def fibonacci(n):
    zeros = [1,0,1]
    ones = [0,1,1]

    if n >= 3:
        for i in range(2, n):
            zeros.append(zeros[i]+zeros[i-1])
            ones.append(ones[i]+ones[i-1])
        
    print(f"{zeros[n]} {ones[n]}")


if __name__ == "__main__":
    T = int(input())
    for _ in range(T):
        n = int(input())
        fibonacci(n)

 

zeros는 0이 출력되는 횟수를 저장하는 리스트이다.

ones는 마찬가지로 1이 출력되는 횟수를 저장하는 리스트이다.

n에 따라 zeros 리스트와 ones 리스트 각각 피보자치 수열의 패턴을 갖는다.

0부터 2까지의 경우는 미리 리스트에 저장해두고,

n이 3이상인 경우부터 range(2,n) 동안 각각 리스트에 값을 피보나치수열대로 추가한다.

n번째에 대해서 zeros[n] 과 ones[n]을 출력하고 함수가 종료된다.

 

값이 수열이다보니 규칙을 찾아서 더욱 더 짧게 코딩하는 예시도 보았다.

나는 아직 문제를 처음 읽으면 그 문제 그대로 풀려는 경향이 있는데

조금 더 깊게 생각해서 다른 방법으로 풀 수는 없는 지, 숨겨진 규칙들은 없는 지, 조금 더 짧고 쉽고 간결하게 코드를 짤수는 없는 지 고민해보는 연습을 해야겠다

 

'Algorithm' 카테고리의 다른 글

[백준] 1072 게임 / python  (0) 2024.04.10
[백준] 1735 분수 합  (0) 2024.04.08
[백준] 1929 소수 구하기 / python  (2) 2024.04.07
[백준] 2346 풍선 터뜨리기 / python  (0) 2024.04.05
[백준] 2559 수열 / python  (0) 2024.04.05

입력

첫째 줄에 자연수 M과 N이 빈 칸을 사이에 두고 주어진다. (1 ≤ M ≤ N ≤ 1,000,000) M이상 N이하의 소수가 하나 이상 있는 입력만 주어진다.

출력

한 줄에 하나씩, 증가하는 순서대로 소수를 출력한다.

 

코드

import sys
input = sys.stdin.readline

m, n = map(int, input().split())

for i in range(m, n+1):
    if i == 1:
        continue
    for j in range(2, int(i**0.5)+1):
        if i % j == 0 :
            break
    else:
        print(i)

 

1은 소수가 아니므로 제외해준다.

특정 수의 제곱근을 구해, 그 제곱근까지의 약수를 구하면 해당 약수를 포함하는 수를 모두 제거할 수 있다.

i=15일 때 15의 약수는 1 3 5 15

int(sqrt(15)) = 3이고 15는 3으로 나누어 떨어지기 때문에 더 이상 검사할 필요가 없다.

i가 j로 나누어 떨어지므로 약수가 존재한다. => 소수가 아니고 해당 제곱근(j) 이상의 숫자에 대해 더 이상 검사할 필요가 없으므로 멈춘다.

처음에 import math를 하고 math.sqrt(i)로 했는데 그거 때문인 지는 잘 모르겠지만 시간초과가 나왔다.

 

에라토스테네스의 체

: 소수를 구하는 간단하고 효율적인 방법

2부터 시작하여 차례로 모든 수의 배수를 제거해가면서 소수를 찾는다.

예를 들어, 2는 소수이므로 2의 배수를 모두 제거한다.

그 다음으로 남은 수 중 가장 작은 수는 3이므로 3의 배수를 모두 제거한다.

이 방법을 사용하면 비교적 큰 범위 내의 소수를 효율적으로 찾을 수 있다.

모든 숫자를 검사할 필요가 없으므로 시간 복잡도도 O(N) -> O(N^(1/2))로 감소한다.

'Algorithm' 카테고리의 다른 글

[백준] 1735 분수 합  (0) 2024.04.08
[백준] 1003 피보나치 함수 / python  (1) 2024.04.07
[백준] 2346 풍선 터뜨리기 / python  (0) 2024.04.05
[백준] 2559 수열 / python  (0) 2024.04.05
[백준] 2108번 통계학 / python  (0) 2024.04.04
 

2346번: 풍선 터뜨리기

1번부터 N번까지 N개의 풍선이 원형으로 놓여 있고. i번 풍선의 오른쪽에는 i+1번 풍선이 있고, 왼쪽에는 i-1번 풍선이 있다. 단, 1번 풍선의 왼쪽에 N번 풍선이 있고, N번 풍선의 오른쪽에 1번 풍선

www.acmicpc.net

1번부터 N번까지 N개의 풍선이 원형으로 놓여 있고. i번 풍선의 오른쪽에는 i+1번 풍선이 있고, 왼쪽에는 i-1번 풍선이 있다. 단, 1번 풍선의 왼쪽에 N번 풍선이 있고, N번 풍선의 오른쪽에 1번 풍선이 있다. 각 풍선 안에는 종이가 하나 들어있고, 종이에는 -N보다 크거나 같고, N보다 작거나 같은 정수가 하나 적혀있다. 이 풍선들을 다음과 같은 규칙으로 터뜨린다.

우선, 제일 처음에는 1번 풍선을 터뜨린다. 다음에는 풍선 안에 있는 종이를 꺼내어 그 종이에 적혀있는 값만큼 이동하여 다음 풍선을 터뜨린다. 양수가 적혀 있을 경우에는 오른쪽으로, 음수가 적혀 있을 때는 왼쪽으로 이동한다. 이동할 때에는 이미 터진 풍선은 빼고 이동한다.

예를 들어 다섯 개의 풍선 안에 차례로 3, 2, 1, -3, -1이 적혀 있었다고 하자. 이 경우 3이 적혀 있는 1번 풍선, -3이 적혀 있는 4번 풍선, -1이 적혀 있는 5번 풍선, 1이 적혀 있는 3번 풍선, 2가 적혀 있는 2번 풍선의 순서대로 터지게 된다.

입력

첫째 줄에 자연수 N(1 ≤ N ≤ 1,000)이 주어진다. 다음 줄에는 차례로 각 풍선 안의 종이에 적혀 있는 수가 주어진다. 종이에 0은 적혀있지 않다.

출력

첫째 줄에 터진 풍선의 번호를 차례로 나열한다.

 

코드

import sys
from collections import deque
input = sys.stdin.readline

n = int(input())
deq = deque(enumerate(map(int, input().split()), start = 1))
result = []

while deq:
    idx, paper = deq.popleft()
    result.append(idx)

    if paper > 0:
        deq.rotate(-(paper-1))
    elif paper < 0:
        deq.rotate(-paper)
    
print(' '.join(map(str, result)))

 

deque를 활용하여 코드를 구현했다.

 

enumerate

터진 풍선의 번호를 출력하는 문제이기 때문에 pop을 하더라도 초기 인덱스 정보는 끝까지 유지되어야 한다. 처음에 이 부분에서 헷갈렸던 것 같다. 

  • enumerate 사용 전
    deque([3, 2, 1, -3, -1])
  • enumerate 사용 후
    deque([(0, 3), (1, 2), (2, 1), (3, -3), (4, -1)])

덱에 인덱스와 종이 값이 튜플로 묶여서 하나의 원소로 저장된다.

enumerate() 함수는 기본적으로 0부터 시작하는 인덱스를 할당한다.

두번째 매개변수를 사용하여 시작 인덱스를 지정할 수도 있다. (start=1)

이렇게 enumerate() 함수를 사용하면 반복문에서 인덱스와 요소를 동시에 사용할 수 있다.

 

deque.rotate()

원형큐를 구현할 있다.

rotate(-1)은 원형 큐를 반시계방향으로 1칸 회전시키고,

rotate(1)은 원형 큐를 시계방향으로 1칸 회전시킨다.

 

+ Recent posts