[인프런] 파이썬 알고리즘 문제풀이 입문 - 코드 구현력 기르기 (수정중)

bee·2023년 3월 4일
0

Algorithm

목록 보기
1/8
post-thumbnail

1. K번째 약수

두 개의 자연수 N과 K가 주어졌을 때, N의 약수들 중 K번째로 작은 수를 출력하는 프로그램을 작성하시오.

## 내 풀이
N, K = map(int,input().split())
d = []

for i in range(1, N+1):
	if N % i == 0:
    	d.append(i)

print(d[K-1])

if len(d) < K : 
	print(-1)

6 3
3

## 모범답안
N, K = map(int, input().split())
cnt = 0 # 약수의 개수

for i in range(1, N+1):
	if N % i == 0:
    	cnt += 1
    if cnt == K :
    	print(i)
        break # for문에 정상적으로 끝났다 = 약수를 찾지 못했다
else : # 약수를 찾지 못했을때
	print(-1)

핵심은 for ~ else 구문을 활용하는 것이었다. for ~ else문은 for문이 break 등으로 중간에 끊기지 않고 정상적으로 실행되었을 경우 else문이 실행된다. 즉 이 문제에서는 for문이 정상적으로 끝났을 경우(=약수를 찾지 못했을 때), else문이 실행되면서 -1을 출력하게 된다. 내가 푼 풀이도 맞기는 하지만, 좀 더 체계적으로 코드를 작성하려면 이번 문제에서는 for ~ else문 사용이 핵심 포인트였던 것!


2. K번째 수

N개의 숫자로 이루어진 숫자열이 주어지면 해당 숫자열 중에서 s번째부터 e번째 까지의 수를 오름차순 정렬했을 때 k번째로 나타나는 숫자를 출력하는 프로그램을 작성하세요.

## 내 풀이
T = int(input())

for i in range(T):
	N, s, e, k = map(int, input().split())
	num = list(map(int, input().split()))

	for j in range(num[s-1], num[e]):
    	num.append(j)
        
    num.sort()
    print(num[k-1])

2
6 2 5 3
5 2 7 3 8 9
3
15 3 10 3
4 15 8 16 6 6 17 3 10 11 18 7 14 7 15
6

## 모범답안
T = int(input())

for t in range(T):
	N, s, e, K = map(int, input().split())
    num = list(map(int, input().split()))
    num = [s-1 : e]
    num.sort()
    print(num[K-1])

2
6 2 5 3
5 2 7 3 8 9
7
15 3 10 3
4 15 8 16 6 6 17 3 10 11 18 7 14 7 15
6

핵심은 '리스트 슬라이스' 였다. 여기서 주의할 점은 k번째 원소를 말하더라도 인덱싱의 규칙을 생각하면 리스트의 k-1번째 원소를 지칭하는 것이다. 또 한 가지는 테스트케이스 별 실행이 필요하기 때문에 큰 틀로 for문을 활용하여 하나의 테스트케이스를 돌고 그 다음 테스트케이스를 실행할 수 있도록 해야한다는 것이다. 이 점을 주의해서 문제를 푼다면 충분히 풀 수 있는 문제였다.


3. K번째 큰 수

1부터 100사이의 자연수가 적힌 N장의 카드를 가지고 있고, 같은 숫자의 카드가 여러장 있을 수 있다. 이 중 3장을 뽑아 각 카드에 적힌 수를 합한 값을 기록하려고 하며, 3장을 뽑을 수 있는 모든 경우를 기록한다. 기록한 값 중 K번째로 큰 수를 출력 하는 프로그램을 작성하시오.

## 내 풀이
N, K = map(int, input().split())
card = list(map(int, input().split()))

# 뽑은 카드 3장의 값을 합한 조합
cardsum = []
for i in range(0, len(card)-2):
    for j in range(i+1, len(card)-1):
        for k in range(j+1, len(card)):
            s = card[i] + card[j] + card[k]
            cardsum.append(s)

# 내림차순 정렬
cardsum.sort(reverse=True)

# K번째 수 출력 (중복제외)
print(cardsum[K-1])

10 3
13 15 34 23 45 65 33 11 26 42
143

## 모범답안
N, K = map(int, input().split())
card = list(map(int, input().split()))

cardsum = set() # 중복을 제거하는 자료구조
for i in range(N):
    for j in range(i+1, N):
        for k in range(j+1, N):
            cardsum.add(card[i]+card[j]+card[k]) # set자료구조는 append가 아닌 add를 사용

cardsum = list(cardsum) # set자료구조는 sort를 사용할 수 없으므로 list로 변환
cardsum.sort(reverse=True)
print(cardsum[K-1])

손으로 가능한 합의 조합 몇 개만 적어봐도 금방 규칙을 찾을 수 있었다. for문의 range범위를 정할때 헷갈려서 직접 눈으로 규칙을 확인해보았다.

이 문제의 또 한 가지 핵심 포인트는 set자료구조의 사용이었다. 제일 처음에 풀었을 때는 sum을 리스트의 형태로 잡고 풀었는데, 문제를 읽어보면 중복되는 숫자가 나올 수는 있지만, 우리가 구해야하는 것은 중복없이 큰 순으로 K번째의 합을 구하는 것이다. 중복을 허용하지 않는 set(집합) 자료구조의 특징을 고려하면서 풀면 쉽게 풀릴 수 있는 문제였다. 집합에서는 원소를 추가할 때 .append()가 아니라 .add()를 사용해야하고, 집합은 .sort()를 적용할 수 없기 때문에 리스트로 변환 후 사용해야하는 것도 잊지말자!


4. 대표값

N명의 학생의 수학점수가 주어질 때, N명의 학생들의 평균(소수 첫째자리 반올림)을 구하고, N명의 학생 중 평균에 가장 가까운 학생은 몇 번째 학생인지 출력하는 프로그램을 작성하시오. (단, 평균과 가장 가까운 점수가 여러 개일 경우 먼저 점수가 높은 학생의 번호를 답으로 하고, 높은 점수를 가진 학생이 여러 명일 경우 그 중 학생번호가 빠른 학생의 번호를 답으로 한다.)

## 내 풀이
N = int(input()) # 학생수
score = list(map(int, input().split()))
avg = sum(score)/len(score)
round(avg, 0)

bias = list(map(lambda x:abs(x-avg), score))
a = min(bias)

모르겠음..

## 모범답안
N = int(input())
s = list(map(int, input().split()))
avg = int((sum(a)/n) + 0.5)
min = 2147000000

for idx, x in enumerate(s):
	tmp = abs(x-avg) # 편차 절댓값(=평균과의 거리)
    
    if tmp < min:
    	min = tmp # 최솟값은 tmp가 된다
        score = x # score : 답이되는 점수
        res = idx + 1 # res : 답이되는 점수의 학생번호
    elif tmp == min # 같은 점수차를 갖는 학생들이 있다면
    	if x > score: # 그 중 더 높은 점수를 갖는 학생들에 한해서
        	score = x
            res = idx + 1

print(avg, res)

10
45 73 66 87 92 67 75 79 75 80
74 7

핵심은 '최솟값 구하기' 알고리즘과 조건을 잘 활용하는 것이었다.

+) 파이썬에서의 반올림

파이썬에서의 round함수는 round_half_even방식을 택한다. 즉, 파이썬에서의 round함수는 우리가 일반적으로 알고있는 5이상의 수를 올림해주는 round_half_up의 방식을 따르지 않는다는 것이다. 따라서 round함수를 써도 5이상의 수가 올림되지 않을 수 있다. 정확하게 .5000000 의 지점에 있다면 짝수(even)쪽으로 근사하게 된다.
예시로 알아보자.

## round_half_even
a = 4.500
print(round(a))
b = 5.5000
print(round(b))

4
6
round_half_up의 방식을 따랐다면 원래 a는 5가 나와야 하지만, 파이썬에서는 4가 나온다.

a = 4.511
print(round(a))

5

## round_half_even 해결방법
a = 66.5
a = a + 0.5 
a = int(a)
print(a)

67
즉, 반올림하려는 수에 +0.5를 한 후 int화 시켜서 소수점을 날리면 우리가 원하는 round_half_up 방식의 반올림을 할 수 있다.


5. 정다면체

두 개의 정 N면체와 정 M면체의 두 개의 주사위를 던져서 나올 수 있는 눈의 합 중 가장 확률이 높은 숫자를 출력하는 프로그램을 작성하시오. (정답이 여러 개일 경우 오름차순으로 출력하시오.)

## 내 풀이
N, M = map(int, input().split())
ch = [0] * (N*M) + 3
res = []


print(res)
## 모범답안

(수정중)

profile
벌집처럼 밀도있게 차곡차곡 쌓아나가는중

0개의 댓글