카테고리 없음

코딩테스트 with <이코테>, Greedy algorithm

oogieon_n_on 2022. 7. 23. 16:38

당장 좋은 것만 선택하는 그리디 

1.  동빈이의 큰수의 법칙 

- 떠올려야 할 아이디어 :
    1. 가장 큰 수를 k번 더하고 더할때마다 남은 카운트를 줄인다.  
       k 카운트가 다 차면 두번째 큰수로 넘어가서 한번을 더해주고 다시 큰수를 
       k 만큼 카운트 한다. 이 과정을 m의 남은 카운트가 0이 될때 까지 반복한다. 
    - 입력예시 : 5 8 3
               2 4 5 4 6

    - 출력예시 : 46
# naive solution

# n = 배열의 크기 , m = 숫자가 더해지는 횟수  , k = 최대 반복 수  
n, m, k = map(int,input().split())

# n = 5, m= 8 , k =3

data = list(map(int, input().split()))

data.sort()
first = data[n-1] # 가장 큰 수
second = data[n-2] # 그 다음 큰 수

result = 0

# N = [2,4,5,4,6], 5

while True: 
    for i in range(k): # i = 0,1,2 3번만 하도록 
        if m == 0:
            break
        result += first # + 제일큰수 , result += 6 , += 12 ...
        m -= 1 # 더한횟수-1

        # i = 0 :  result(0) + fisrt(6) = 6, m(8) -1 = 7
        # i = 1 : result(6) + 6 = 12, m(7) -1 = 6
        # i = 2 : result(12) + 6 = 18, m(6) -1 = 5
      
        # 다시 돌아와서 0,1,2 돌리고
    if m == 0: 
        break
    result += second # result (18) + second(5) = 23, 
    m -= 1 # m(5) -1 = 4

        # 다시 돌아와서 second 더해주면 
        # m==0이므로 break loop 
print(result)

책에서 제시된 효율적인 해결법 :  반복되는 수열에 대한 파악! 

# 수열의 길이 = K + 1, M을 수열의 길이로 나눈 몫 = 수열이 반복되는 횟수 

# 수열 반복되는 횟수 * k = 가장 큰 수가 등장하는 횟수 

# 나누어 떨어지지 않는 경우, M을 K+1로 나눈 나머지 만큼이 가장 큰 수가 추가로 더해짐
n,m,k = map(int, input().split())
data = list(map(int, input().split()))

data.sort()
first = data[-1]
second = data[-2]

count = int(m / (k+1)) * k # 몫 * k만큼 큰수가 더해짐 
count += m % (k+1) # 나머지가 있다면 그 만큼 추가로 더해짐
                   # 나머지가 0이라면 0이 더해지므로 상관없음 

result = 0 
result += count * first
result += (m-count) * second 

print(result)

2.  숫자 카드 게임 

- 게임룰 : n*m 형태로 놓여있는 카드배열에서 가장 큰 숫자를 가진 카드를 뽑기  
         단, 속해있는 행을 먼저 고른후 그 행에서는 가장 작은 카드를 뽑아야  
         한다.  
         즉, 최소값이 가장 큰 행을 뽑도록 프로그래밍 해야한다.
- 입력예시

        3 3  # (n * m)
        3 1 2  # 실제 카드 입력
        4 1 4  
        2 2 2 

- 출력예시
        2

예시 솔루션 

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

result = 0

for i in range(n): # n개의 행만큼 반복
    data = list(map(int, input().split())) # 첫 입력 리스트로 받기 
    
    min_value = min(data) # 입력받은 리스트중 가장 작은 값 찾기 
    
    result = max(result, min_value) # result와 min_value중 작은값 받기

print(result)

2중 반복문 구조를 이용할 수도 있다. 

# 2중 반복문 구조 이용하기 

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

result = 0 

for i in range(n):
    data = list(map(int,input().split()))
    
    min_value = 10001
    
    for a in data:
        min_value = min(min_value, a)
    
    result = max(result, min_value)

print(result)

3.  1이 될 때까지

- 게임룰: 어떠한 수 N이 1이 될 때까지 다음의 두 과정중 선택하여 반복적으로 수행 
         1. N에서 1을 뺸다
         2. 나누어 떨어진다면, N을 K로 나눈다.

- 입력예시:
         23 3 # n k 
- 출력예시: 
         6

내가 풀어본 솔루션 

# 나의 첫 솔루션 

n,k = map(int, input().split())
count = 0 

# 23 3 

while n >= k: 
    minus = n % k  # 2 
                                                           
    n -= minus # 나머지 만큼 뺀 값으로 n을 update
    count += minus # 나머지 빼준 만큼 count , count +=2 = 2
                                 
    divde_k = n / k # 21 / 3 = 7 
    n = divde_k # n을 7로 update 
    count += 1 # 나눠줬으니 count +=1 = 3 

if n>1: # 3: n=2, k=3 , into loop 
    n -= 1 #3: 2-1 = 1
    count += 1 # count +=1 = 6


print(int(count))

책에서 제시한 효율적인 해결법 

# 효율적인 해결법
n,k = map(int, input().split())
result = 0

# 23 3

while True: # 나누어떨어질때 까지 1 빼기 
    target = (n//k)*k # n과 가까운 k의 가장 큰 배수를 target에 저장 
    result += (n-target) # n에서 target을 뺀 값(나머지)를 카운트  
    n = target # n을 가장 큰 배수로 update
    
    if n < k:
        break
    result += 1
    n //= k

result += (n-1)

print(result)