728x90

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

 

1041번: 주사위

첫째 줄에 N이 주어진다. 둘째 줄에 주사위에 쓰여 있는 수가 주어진다. 위의 그림에서 A, B, C, D, E, F에 쓰여 있는 수가 차례대로 주어진다. N은 1,000,000보다 작거나 같은 자연수이고, 쓰여 있는 수

www.acmicpc.net

 

문제)

 

풀이)

n이 1개부터 4개까지 그림을 그려가면서 해결해 보려 했는데 규칙성을 발견하지 못하고 헤맸던 문제이다.

그래서 한참을 고민ㅠㅠ

 

 

이 문제를 풀기 위해서는 크게 3가지 단계로 이루어진다.

 

 

  1. 정육면체에서 한 주사위당 보일 수 있는 면의 개수는 1, 2, 3 중 하나임을 파악한다.
  2. 보이는 면의 개수당 해당 주사위의 개수를 구하는 규칙성을 찾는다.
  3. 주사위에서 마주보는 면을 제외하고 나머지는 모두 인접하다. 이를 이용해 보이는 면에 해당하는 최소 값을 구한다.

 

1 단계: 정육면체에서 한 주사위당 보이는 면의 개수

주사위를 정육면체로 쌓아 올렸을 때, 바깥쪽에 보이는 주사위의 면의 개수는 총 3가지다.

 

3개: 가장 쉽게 생각해 볼 수 있는 것으로 맨 상단(뚜껑)에 존재하는 가장 끝의 4개는 3개의 면이 보인다.

2개: 모서리 쪽에 위치한 부분 4세트 + 상단에 위치한 부분 4세트

1개: 상단의 가운데 부분 + 나머지 4면에서 가운데 부분

 

 

 

2 단계: 주사위 개수 구하는 규칙성 찾기

 

해당 그림은 4*4*4 짜리 정육면체를 그려보았다.

쓰여 있는 숫자는 해당 주사위가 보이는 면의 개수이고, 2개의 면이 보이는 곳은 초록색 형광표시했다.(2개짜리 개수 구하기 편하려고!)

 

 

 

 

 

 

 

 

  • 3개: 맨 상단 4개 => 4
  • 2개: 모서리 쪽에 위치한 부분(N-1) 4세트 + 맨 상단(N-2) 4세트 => 4(N-2) + 4(N-1)
  • 1개: 상단 가운데: (N-2)*(N-2) + 4면 가운데: (N-2)*(N-1) => (N-2)*(N-2) +4(N-2)*(N-1)

 

 

3 단계: 면의 최소값 구하기

주사위는 6면으로 마주보는 쌍을 구하면 3쌍이다.

이 3쌍 중에서 가장 작은 수를 각각 저장하면 6개 중 3개가 저장된다.

 

이를 오름차순으로 정렬후, 3면이 보이는 것은 3개를 모두 더한 것, 2면이 보이는 것은 앞에서부터 2개를 더한 것, 1면이 보이는 것은 제일 처음 부분을 사용하면 된다.

 

그 후 각 면이 보이는 주사위 개수별로 곱해준 후 모두 더한 후 출력하면 문제가 해결된다.

 

 

 

n = int(input())
dice = list(map(int, input().split()))

# 1, 2, 3면이 보이는 주사위 개수 세기
side1 = 4*(n-2)*(n-1) + (n-2)**2
side2 = 4*(n-2) + 4*(n-1)
side3 = 4
# 주사위에서 마주보는 면 중 작은 것 저장하는 리스트
min_li = []

if n == 1:
    print(sum(dice) - max(dice))

else:
    # 주사위의 마주보는 면을 쌍으로 만들고 그 중 작은 애 뽑아 저장
    # (마주 보는 애는 인접하지 않으므로 동시에 보일 수 없음)
    min_li.append(min(dice[0], dice[5]))
    min_li.append(min(dice[1], dice[4]))
    min_li.append(min(dice[2], dice[3]))
    min_li.sort()

    min1 = side1 * min_li[0]
    min2 = side2 * (min_li[0] + min_li[1])
    min3 = side3 * (min_li[0] + min_li[1] + min_li[2])

    s = min1 + min2 + min3
    print(s)
728x90

'코테 > greedy' 카테고리의 다른 글

[Greedy] 1이 될 때까지  (0) 2022.03.14
[Greedy] 숫자 카드 게임  (0) 2022.03.13
[Greedy] 큰 수의 법칙  (0) 2022.03.04
[Greedy] 거스름돈  (0) 2022.03.04
728x90

"이것이 코딩테스트다" 책 문제 _ greedy 책 문제

문제)

어떠한 수 N이 1이 될 때까지 다음의 두 과정 중 하나를 반복적으로 선택하여 수행하려고 한다. 단, 두 번째 연산은 N이 K로 나누어 떨어질 때만 선택할 수 있다.

 

  1. N에서 1을 뺀다.

  2. N을 K로 나눈다.

 

N과 K가 주어질 때 N이 1이 될 때까지 1번 혹은 2번의 과정을 수행해야 하는 최소 횟수를 구하는 프로그램을 작성해라.

 

입력조건

  • 첫째 줄에 N(2 <= N <= 100,000)과 K(2 <= K <= 100,000)가 공백으로 구분되며 각각 자연수로 주어진다. 이때 입력으로 주어지는 N은 항상 K보다 크거나 같다.

 

출력조건

  • 첫째 줄에 N이 1이 될 때까지 1번 혹은 2번의 과정을 수행해야 하는 횟수의 최솟값을 출력한다.

 

 

 

풀이)

N이 1이 되기 위해 횟수가 가장 최소가 되려면 빼는 것 보다 나누는 과정이 숫자를 1에 가장 빨리 도달하게 할 수 있는 방법이다.

즉, 나누기를 최대한 많이 실행하면 된다.

따라서 N을 K로 나누어 떨어질 때까지 뺀 후, K로 나누는 과정을 반복하면 된다.

 

예를 들어 N = 17, K = 3일 경우 연산 과정은 다음과 같다.

단계 연산 과정 N
0(초기)   N = 17
1 1 빼기 N = 16
2 1 빼기 N = 15
3 K로 나누기 N = 5
4 1 빼기 N = 4
5 1 빼기 N = 3
6 K로 나누기 N = 1

 

이를 구현한 코드는 다음과 같다.

N, K = map(int, input().split())
count = 0

while (N > 1) :
    if N % K == 0:
        N = N // K
    else:
        N -= 1
    count += 1

print(count)

N이 1이 될 때까지 진행하기 때문에 while문의 조건은 N이 1보다 클 때이다.

while문 내에서 N이 K로 나누어 떨어질 경우 N은 K로 나누었을 때 몫으로 되고, K로 나누어 떨어지는 경우가 아닐 때 N에서 1씩 뺀다.

이때 연산 횟수를 count 변수에 1씩 더하여 저장한다.

 

 

 

문제에서는 N의 범위가 10만 이하이므로, 일일히 1씩 빼도 문제가 없지만, N이 100억 이상일경우 효율성 문제가 발생할 수 있다. 따라서 N이 K의 배수가 되도록 한 번에 빼는 방식으로 작성할 수 있다.

 

정답 예시)

N, K = map(int, input().split())
count = 0

while True:
    target = (N // K) * K
    count += N - target
    N = target

    if N < K:
        break
    count += 1
    N //= K

count += (N - 1)
print(count)

target을 N보다 작지만 가장 큰, K로 나누어 떨어지는 수이고, count에 N과 target의 차이(N에서 1을 뺀 횟수)만큼 더해준다.

그리고 N을 다시 target으로 갱신해주고 K로 나누어준다. 이때, N이 K보다 작을 경우(몫이 계속 0이 되므로) while문에서 빠져나온다.

그리고 N이 1이 되기까지 횟수이므로 (N-1)만큼 count에 더해주면 된다.(N을 더할 경우 N이 0이 되어버린다.)

728x90

'코테 > greedy' 카테고리의 다른 글

[Greedy] 백준 1041 주사위  (0) 2022.06.05
[Greedy] 숫자 카드 게임  (0) 2022.03.13
[Greedy] 큰 수의 법칙  (0) 2022.03.04
[Greedy] 거스름돈  (0) 2022.03.04
728x90

"이것이 코딩테스트다" 책 문제 _ greedy 책 문제

문제)

여러 개의 숫자 카드 중에서 가장 높은 숫자가 쓰인 카드 한 장을 뽑는 게임이다.

단, 게임의 룰을 지키며 카드를 뽑아야 하고 룰은 다음과 같다. 게임의 룰에 맞게 카드를 뽑는 프로그램을 만들어라.

 

   1. 숫자가 쓰인 카드들이 N * M 형태로 놓여 있다. 이때 N은 행의 개수를, M은 열의 개수를 의미한다.

   2. 먼저 뽑고자 하는 카드가 포함되어 있는 행을 선택한다.

   3. 그다음 선택된 행에 포함된 카드들 중 가장 숫자가 낮은 카드를 뽑아야 한다.

   4. 따라서 처음에 카드를 골라낼 행을 선택할 때, 이후에 해당 행에서 가장 숫자가 낮은 카드를 뽑을 것을 고려하여 최종적으로 가장 높은 숫자의 카드를 뽑을 수 있도록 전략을 세워야 한다.

 

입력조건

  • 첫째 줄에 숫자 카드들이 놓인 행의 개수 N과 열의 개수 M이 공백을 기준으로 하여 각가 자연수로 주어진다. (1 <= N, M <= 100)
  • 둘째 줄부터 N개의 줄에 걸쳐 각 카드에 적힌 숫자가 주어진다. 각 숫자는 1이상 10,000 이하의 자연수다.

 

출력조건

  • 첫째 줄에 게임의 룰에 맞게 선택한 카드에 적힌 숫자를 출력한다.
입력 예시1)
3 3
3 1 2
4 1 4
2 2 2
출력 예시1)
2
입력 예시2)
2 4
7 3 1 8
3 3 3 4
출력 예시2)
3

 

 

 

풀이)

각 행마다 가장 작은 수를 찾은 후 그 중에서 가장 큰 수를 출력하면 된다.

N, M = map(int, input().split())
minimum = []
for i in range(N):
    L = list(map(int, input().split()))
    minimum.append(min(L))
print(max(minimum))

N, M을 입력 받아서 정수로 변환해주고, 각 행에서 가장 작은 수를 저장할 minimum이라는 리스트를 생성한다.

행의 수만큼 반복하여 각 행을 입력받으면 L이라는 list로 받고, min()을 이용하여 가장 작은 수를 minimum에 추가한다.

모든 행의 작은 수를 추가한 이후, max()를 이용하여 가장 큰 수를 출력해주면 된다.

 

이 문제는 앞에서 나온 문제에 비하면 매우 쉬운 난이도고, 답에서 나온 풀이도 비슷하기 때문에 생략한다.

728x90

'코테 > greedy' 카테고리의 다른 글

[Greedy] 백준 1041 주사위  (0) 2022.06.05
[Greedy] 1이 될 때까지  (0) 2022.03.14
[Greedy] 큰 수의 법칙  (0) 2022.03.04
[Greedy] 거스름돈  (0) 2022.03.04
728x90

"이것이 코딩테스트다" 책 문제 _ greedy 책 문제

문제)

다양한 수로 이루어진 배열이 있을 때 주어진 수들을 M번 더하여 가장 큰 수를 만든다. 이때 배열의 특정한 인덱스(번호)에 해당하는 수가 연속해서 K번을 초과하여 더할 수 없다. 입력 조건에 따라 결과를 출력하시오.

 

 

입력 조건

  • 첫째 줄에 N(2<=N<=1,000), M(1<=M<=10,000), K(1<=K<=10,000)의 자연수가 주어지며, 각 자연수는 공백으로 구분한다.
  • 둘째 줄에 N개의 자연수가 주어진다. 각 자연수는 공백으로 구분한다. 단, 각각의 자연수는 1 이상 10,000 이하의 수로 주어진다.
  • 입력으로 주어지는 K는 항상 M보다 작거나 같다.

 

출력 조건

  • 첫째 줄에 큰 수의 법칙에 따라 더해진 답을 출력한다.

 

입력 예시)
5 8 3
2 4 5 4 6
출력 예시)
46

 

 

풀이)

greedy 방법을 사용하여 가장 큰 수를 K번 만큼 더하고, 두 번째로 큰 수를 한 번 더하는 연산을 반복하면 된다.

 

입력 예시에 따르면 다음과 같다.

6+6+6+5+6+6+6+5 = 46

 

1. 내가 작성한 코드

N, M, K = map(int, input().split())
n = sorted(list(map(int, input().split())), reverse=True)
answer = 0
count = 1
for i in range(M):
    if count > K:
        answer += n[1]
        count = 1
    else:
        answer += n[0]
        count += 1
print(answer)

 

첫째 줄 입력인 N, M, K를 input()을 이용하여 받고, 공백 기준으로 구분하므로 split을 사용한다. 이때 map 함수를 이용하여 문자열을 정수로 바꿔준다.

둘째 줄 입력 또한 같은 방법으로 받되 list를 생성하고 내림차순으로 정렬한다. 

count는 몇 번 더해졌는지 확인하기 위한 변수로 1로 설정해준다.

M번 반복하면서 count가 K보다 큰지 확인한다. (count가 0일 경우 K와 같은지 확인하면 됨)

만약 K번 이내라면, 가장 큰 수를 정답에 더해주고, 아닐 경우 두 번째로 큰 수를 더해준 후 count를 다시 1로 설정해준다.

 

 

2. 정답 예시

n, m, k = map(int, input().split())
data = list(map(int, input().split()))
data.sort()

first = data[n-1]
second = data[n-2]
result = 0

while True:
    for i in range(k):
        if m == 0:
            break
        result += first
        m -= 1
    if m == 0:
        break
    result += second
    m -= 1

print(result)

 

 

3. 정답 예시2 _ 수열처럼 생각

 

가장 큰 수를 K번 만큼 더하고, 두 번째로 큰 수를 한 번 더하는 과정을 반복할 때, 규칙이 생긴다.

이 규칙을 수열처럼 생각한다면 효율성을 높일 수 있다.

 

입력 예시에서, 

6+6+6+5 + 6+6+6+5 = 46

이렇게 노란색 박스 부분이 반복되는 수열임을 생각할 수 있다.

수열의 길이는 '가장 큰 수 K번 + 두번째로 큰 수 1 번 = K+1'이다.

이 수열은 M을 수열의 길이로 나눈 몫(M // (K+1)) 만큼 더해지고, 나머지 부분(M % (K+1))은 가장 큰 수로만 더해주면 된다. 

 

가장 큰 수를 더하는 횟수: M // (K+1) * K + M % (K+1)

두 번째로 큰 수를 더하는 횟수: M - 가장 큰 수를 더하는 횟수

결과(정답): 위 두 결과를 합한 값

 

따라서 2번 코드를 변형하여 코드를 완성해보면 다음과 같다.

# 수열 처럼 생각
n, m, k = map(int, input().split())
data = list(map(int, input().split()))
data.sort()

first = data[n-1]
second = data[n-2]

count = m //(k+1) * k + m % (k+1)

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

print(result)

 

728x90

'코테 > greedy' 카테고리의 다른 글

[Greedy] 백준 1041 주사위  (0) 2022.06.05
[Greedy] 1이 될 때까지  (0) 2022.03.14
[Greedy] 숫자 카드 게임  (0) 2022.03.13
[Greedy] 거스름돈  (0) 2022.03.04
728x90

"이것이 코딩 테스트다" 책 문제 _ greedy 파트

문제)

카운터에 거스름돈으로 사용할 500원, 100원, 50원, 10원짜리 동전이 무한히 존재한다고 가정하자.

손님에게 거슬러 줘야 할 돈이 N원일 때 거슬러줘야 할 동전의 최소 개수를 구해라. 단, N은 10의 배수.

 

풀이)

def sol(N):
    answer = 0
    coin = [500,100,50,10]
    count = N
    for c in coin:
        answer += count//c
        count %= c
    """    count = count - count//c*c"""
    return answer

print(sol(1260))    # answer: 6

 

예를 들어 N = 1260인 경우, greedy 방법을 사용하면 다음과 같다.

1260 = 500 * 2 + 260

260 = 100 * 2 + 60

60 = 50 * 1 + 10

10 = 10 * 1 + 0

=> 정답: 2+2+1+1 = 6

 

위와 같이 가장 큰 동전을 지불하고, 남는 금액을 적은 금액이 지불하는 방식이다.

따라서 정답은 몫을 모두 더한 값이 된다.

 

코드 설명)

4 종류 동전을 'coin' list에 넣는다. 이때, greedy 방법(큰 액수부터 거슬러주기)을 쓸 것이므로 큰 동전부터 list에 저장한다.

count 변수에 N값을 저장한다. (후에 나머지 액수를 저장해줌. 변수 이름 잘못 지었음..ㅜ)

반복문을 이용하여 가장 큰 동전부터 액수를 나눈 몫을 정답에 더한다. 또한 count에 나머지 액수를 갱신해준다.

 

나머지를 이용하면 쉬울걸 나는 count를 갱신할 때  '액수 - 몫 * 동전 금액 ' 이렇게 하고 있었음...ㅠ

 

나머지 문제는 greedy에서 가장 기본적인 문제다!

728x90

'코테 > greedy' 카테고리의 다른 글

[Greedy] 백준 1041 주사위  (0) 2022.06.05
[Greedy] 1이 될 때까지  (0) 2022.03.14
[Greedy] 숫자 카드 게임  (0) 2022.03.13
[Greedy] 큰 수의 법칙  (0) 2022.03.04

+ Recent posts