보통 알고리즘을 작성할 때, 연산 속도와 메모리 공간을 최대한 활용할 수 있도록 효울적으로 작성하는데,

어떤 문제는 메모리 공간을 약간 더 사용하면 연산속도를 비약적으로 증가시킬 수 있는 방법이 있다.

그 방법 중 대표적인 방법이 다이나믹 프로그래밍 기법이다.

 

[ 다이나믹 프로그래밍을 사용할 수 있는 조건 ]
     1. 큰 문제를 작은 문제로 나눌 수 있다.

     2. 작은 문제에서 구한 정답은 그것을 포함하는 큰 문제에서도 동일하다.

 

다이나믹 프로그래밍을 코드로 구현하기 위해서는 메모이제이션 기법을 사용하는데,
그저 다이나믹 프로그래밍 구현 방법 중 하나라고 생각하면 된다.

메모이제이션 기법은 한 번 구한 결과를 메모리 공간에 저장해두고 같은 식을 다시 호출하면

메모한 결과를 그대로 가져오는 기법을 의미한다.

(보통 메모리 공간은 dictionary나 list를 사용하는 것 같다)

 

1. 1로 만들기

나의 생각)
가능한 연산은 2,3,5로 나누는 것과 1로 빼는 것이다.

 

메모리에 저장할 값은 정수 i에서 1 ~ i까지의 연산 최소 횟수정의하였다.

값을 계산하기 위해 arr[0] = 0; arr[1] = 0; arr[2] = 1; arr[3] = 1; arr[4] = 2; arr[5] = 1를 미리 초기값으로 지정하였다.

''' 
input 1:[26]
output: 3 
input 2: [20730, 16465, 27368, 21803, 29081, 23215, 16419, 12244, 16602, 25045]
output 2: 10 13 11 13 14 13 11 11 11 10
input 3: [20730, 16465, 27368, 21803, 29081, 23215, 16419, 12244, 16602, 25045]
output 3: 8 10 9 9 11 11 11 8 11 10 
'''
nums = [8000, 6814, 2553, 3918, 7034, 5806, 5471, 3012, 6986, 5347]

def make_to_one(n):
    arr = [0 for _ in range(n+1)]
    arr[0] = 0; arr[1] = 0; arr[2] = 1; arr[3] = 1; arr[4] = 2; arr[5] = 1

    if n <= 5: 
        print(arr[n])

    operaters = [1,2,3,5]
    for a in range(6, n+1):
        candidates = [30000,30000,30000,30000]
        candidates[0] = arr[a-1] + 1
        if a % 2 == 0:
            candidates[1] = arr[int(a/2)] + 1
        if a % 3 == 0:
            candidates[2] = arr[int(a/3)] + 1
        if a % 5 == 0:  
            candidates[3] = arr[int(a/5)] + 1
        arr[a] = min(candidates)

    print(arr[n], end = ' ')

for num in nums:
    make_to_one(num)

 

2. 개미 전사

주어진 배열에서 한 칸 이상 띄워서 더했을 때 나올 수 있는 최댓값을 구해야 하는 문제이며,

메모리에 저장할 값은 해당 i번째까지 구할 수 있는 최댓값으로 정의하였다.

 

최댓값을 구할 때 나올 수 있는 경우의 수는

     1) 더하지 않을 때: i-1번째에 저장된 값을 저장

     2) 더할 때: i-2번째에 저장된 값에 현재 i값을 더한 값을 저장

 

따라서 i에 저장할 최댓값은 위 경우의 1), 2) 값을 비교하여 나오는 최댓값을 구해서 대입하면 된다.

# n = int(input())
# storage = list(map(int, input().split()))
# print(n, storage)

'''
input 1: 4, [1,3,1,5]
output 1: 8

input 2: 10, [11, 6, 12, 3, 4, 1, 18, 2, 17, 16]
output 2: 62
'''

n, storage = 15, [1, 100, 1, 1, 1, 100, 1, 100, 100, 1, 1, 100, 100, 100, 1]
table_DP = [0 for _ in range(n)]

table_DP[0] = storage[0];
table_DP[1] = max(storage[0], storage[1])

for i in range(2,n):
    candidates = []
    # defore max sum
    candidates.append(table_DP[i-1])
    # front + my sum
    candidates.append(table_DP[i-2] + storage[i])
    table_DP[i] = max(candidates)

print(table_DP[n-1])

 

3. 바닥 공사

숫자에서 뿐만 아니라 도형적인 패턴에서도 큰 문제를 작은 문제로 쪼갤 수 있는지를 판단하기 위한 문제라고 생각한다.

 

메모리에 저장할 값을 가로가 i일 때, 바닥을 채울 수 있는 모든 경우의 수로 정의하였다.

그러나, [i-1]과 [i]와의 관계성을 구하는데 2x1의 덮개에서 중복이 발생해 이를 해결해야 했다.

 

이를 해결하기 위해서는 [i-1]와 [i-2]와 [i]의 관계를 살펴보아야 하는데,

1) [i-1]에서 [i]까지는 2x1만 채울 수 있다.

2) [i-2]에서 [i]까지는 2x1과 1x2와 2x2가 다 채워질 수 있는데,

                              여기서 2x1은 1)의 경우에서 채울 수 있으므로 

총 우리가 고려해야 하는 것은  [i-1]에서 2x1로 채울 때, [i-2]에서 1x2 또는 2x2로 채울 때만 고려하면 된다.

 

!) 아 참, 큰 수로 나누는 이유는 모든 경우의 수는 값이 커지므로 출력 범위를 줄이기 위함이라고 한다.

#n = int(input())
n = 345
div_num = 796796

'''
input:  [760,    765,    378,    345,    4,  521,    741,    640,    464,    530] # n이 들어있는 배열
output: [536911, 448425, 551139, 336313, 11, 373121, 481185, 496871, 646635, 603879] # n에 해당하는 answer가 들어있는 배열
'''

table = [0 for _ in range(1001)] # 1 <= n <= 1000, table = [0] * 1001
table[1], table[2] = 1, 3

for i in range(3, n + 1):
    table[i] = table[i-1] + 2 * table[i-2]

print(table[n] % div_num)

 

4. 효율적인 화폐 구성

이부분을 풀면서 그리디와 DP를 구분해보았는데 여기서 중요한 건 "정당성"이다. 그리디에서는 화폐의 단위가 배수로 커졌기 때문에 큰 수부터 나누는 것이 맞으나, 화폐 단위끼리 간격이 달라진다면 무조건 큰 화폐로 채우는 것이 최선이 아닐 수 있다.

 

예를 들면 화폐 종류가 3개로 1, 7, 11 이렇게 있다고 하자. 우리가 구해야하는 화폐의 금액은 115원이다.

그리디 방식으로 풀게 된다면, '11' 10개, '1' 5개로 총 15개가 필요하다.

그러나, 11+7=18로 115는 18*6 + 7이므로 따라서 '11' 6개 '7' 7개로 총 13개가 필요하다.이로써 화폐 단위가 다를 때 큰 수부터 나누어 구하는 것이 최선의 값을 보장하지 못한다.

 

다시 DP로 돌아오자면, 메모리에 저장할 값은 화폐로 저장할 수 있는 최소 개수로 정의하였다.화폐 종류가 2 하나라면,a) 2부터 n까지 2로 만들 수 있는 i번째에 값을 갱신한다.

 

화폐 종류가 하나 이상이라면,b1) 화폐 선택  b2) a)를 반복하면 된다.

 

이때 갱신할 값은 현재 자신에 채워진 개수 & 자신이 가진 돈에서 화폐 단위만큼 뺐을 때 최소 개수 + 1을 비교해서 작은 값으로 갱신하면 된다.

# n,m = map(int, input().split()) 
# coins = [0] * n
# table = [10001] * (m + 1) # 0 <= m <= 10000
# for i in range(n):
#     coins[i] = int(input());     table[coins[i]] = 1

# input 1 
n, m = 3, 115
coins = [1, 7, 11]
# ouput 1
14

'''
input 2
n, m = 10, 9668
coins = [50, 46, 80, 85, 66, 28, 95, 22, 99, 7] 
output 2
98
input 3
n, m = 10, 5726
coins = [92, 72, 97, 89, 29, 12, 80, 90, 81, 16]
output 3 
60
'''
table = [10001] * (m + 1) # 0 <= m <= 10000

for coin in coins:
    table[coin] = 1

for coin in coins:
    for step in range(coin, m+1):
            table[step] = min(table[step], table[step-coin] + 1)

if table[m] == 10001:
    print("-1")
else:
    print(table[m])

 

자세한 설명은 아래 포스트를 참고하기
masso.tistory.com/80?category=891816

1. 부품 찾기

: 다른 input 만드느라 애 좀 먹었다..ㅠㅜ

 

1-0. input & output

# input 1
N, M = 5, 3
store, client = [8, 3, 7, 9, 2], [5, 7, 9]
# ouput 1
# no yes yes

# input 2
N, M = 50, 20
store = [73,15,26,59,49,44,8,47,63,74,66,81,67,41,52,45,57,4,51,12,10,37,64,7,48,23,42,18,71,55,36,61,83,82,2,20,94,76,28,58,50,92,79,70,96,19,54,97,40,86]
client = [4,35,20,53,93,6,63,52,21,41,5,77,91,78,79,7,46,39,64,95]
# output 2
# yes no yes no no no yes yes no yes no no no no yes yes no no yes no

# input 3
N, M = 60, 60
store = [911,643,184,439,855,981,950,776,519,204,321,47,749,53,851,272,970,270,42,137,523,251,990,171,487,860,796,19,841,46,799,215,333,766,214,709,687,299,334,554,991,396,163,473,869,76,549,128,821,756,703,463,60,283,706,81,314,415,517,375]
client = [500,923,351,303,489,294,579,708,287,382,137,906,229,788,332,251,862,486,795,638,932,725,573,541,329,177,324,868,710,277,290,909,948,364,87,727,652,497,977,618,532,258,949,632,687,29,735,580,617,770,70,946,569,743,189,388,322,636,61,200]
# output 3
# no no no no no no no no no no yes no no no no yes no no no no no no no no no no no no no no no no no no no no no no no no no no no no yes no no no no no no no no no no no no no no no

 

1-1. 정렬한 후 이진 탐색 binary Search

'''=== way1. binary search ==='''
def binary_search(target, start, end, arr):
    if start > end:
        return False
    mid = (start + end) // 2 # 괄호가 없으면 우선 순위가 달라짐
    if arr[mid] == target:
        return True
    elif target < arr[mid]:
        return binary_search(target, start, mid-1, arr) # -1 자꾸 까먹음..!
    else:
        return binary_search(target, mid+1, end, arr) # +1 자꾸 까먹음..!

start_time = timeit.default_timer()
store = sorted(store)
for target in client:
    if binary_search(target, 0, N-1, store):
        print("yes", end = ' ')
    else: 
        print("no", end = ' ')
print()
end_time = timeit.default_timer()
print(">> binary_search time: ", end_time - start_time)

 

1-2. set의 성질 이용하기

'''=== way2. set() ==='''
# N = int(input())
# store = set(map(int, input().split())) #!! list 대신 set으로!
# M = int(input())
# client = list(map(int, input().split()))

set_store = set(store)
start_time = timeit.default_timer()
for target in client:
    if target in set_store:
        print("yes", end = ' ')
    else:
        print("no", end = ' ')
end_time = timeit.default_timer()

 

1-3. 계수 정렬(생략)

 

2. 떡볶이 떡 만들기

: 손님이 원하는 길이에 딱 맞는 최대 높이를 찾을 수 없다면,
  손님이 원하는 길이보다 더 많이 주는 높이 중에서 최댓값을 반환해야한다!! (아래 input 2,3 참고)

 

2-0. input

# input 1
# N, M = 4, 6
# rice_cakes = [19, 15, 10, 17]
# answer:15

# input 2
# N, M = 50, 456
# rice_cakes = [693, 124, 802, 928, 882, 939, 375, 944, 810, 860, 310, 110, 851, 433, 888, 957, 574, 320, 673, 389, 
# 594, 807, 809, 946, 949, 138, 688, 136, 581, 398, 649, 225, 188, 301, 743, 299, 623, 220, 643, 767, 588, 183, 551, 
# 264, 184, 547, 376, 766, 811, 162]
'''절단기에 설정할 수 있는 높이의 최댓값:  872
>> sequential_search time:  0.0007, >> recursive_binary_search time:  0.0003, >> while_binary_search time:  0.0003
'''

# input 3
N, M = 200, 1357
rice_cakes = [273, 20, 157, 49, 24, 224, 175, 193, 169, 153, 141, 297, 258, 287, 268, 154, 211, 185, 113, 294, 129, 117, 242, 91, 
122, 106, 215, 292, 271, 188, 56, 167, 256, 63, 172, 206, 213, 51, 249, 146, 54, 124, 97, 198, 264, 200, 197, 156, 209,
13, 252, 62, 152, 236, 27, 207, 160, 277, 10, 176, 241, 223, 46, 274, 205, 262, 22, 291, 293, 132, 11, 253, 41, 283, 
199, 110, 126, 36, 71, 105, 220, 87, 231, 31, 171, 128, 263, 270, 30, 95, 281, 183, 155, 260, 216, 135, 44, 257, 276, 
104, 254, 279, 217, 12, 299, 55, 149, 138, 58, 125, 246, 266, 66, 201, 75, 85, 57, 77, 229, 101, 133, 21, 35, 161, 240,
286, 131, 290, 99, 130, 88, 184, 261, 247, 83, 151, 84, 238, 81, 221, 267, 265, 89, 173, 123, 245, 79, 26, 102, 225, 
278, 219, 186, 144, 68, 166, 180, 120, 162, 118, 52, 25, 139, 45, 50, 43, 163, 17, 86, 255, 168, 140, 181, 28, 96, 53,
112, 233, 109, 70, 237, 196, 248, 80, 232, 159, 111, 100, 275, 194, 78, 251, 272, 295, 76, 94, 289, 174, 47, 202]
'''절단기에 설정할 수 있는 높이의 최댓값:  240
>> sequential_search time:  0.0011, >> recursive_binary_search time:  0.0005, >> while_binary_search time:  0.0006
'''
# answer = 240

 

2-1. 순차 탐색 sequential Search

'''sequential_search'''
def sequential_search(arr):
    max_H = max(arr)
    while max_H > 0:
        total = 0
        for ele in arr:
            if ele > max_H:
                total += ele - max_H
        if total >= M:
            print("절단기에 설정할 수 있는 높이의 최댓값: ", max_H)
            break;
        max_H -= 1
            
'''%% 입력의 범위가 최대 10억까지의 정수이므로 시간 초과를 받는다.'''  
start_time = timeit.default_timer()
sequential_search(rice_cakes)
end_time = timeit.default_timer()
print(">> sequential_search time: ", format(end_time - start_time, ".4f"))

 

2-2. 이진 탐색 binary Search

'''binary_search'''
def binary_search(ans, arr, minH, maxH):
    global result
    if minH > maxH:
        return -1
    midH = (minH + maxH) // 2
    total = 0
    for ele in arr:
        if ele > midH:
            total += ele - midH
    if total >= ans:
        result = midH
        return binary_search(ans, arr, midH+1, maxH)
    elif total < ans:
        return binary_search(ans, arr, minH, midH-1)

start_time = timeit.default_timer()
result = 0
binary_search(M, rice_cakes, 0, max(rice_cakes))
print("절단기에 설정할 수 있는 높이의 최댓값: ", result)
end_time = timeit.default_timer()
print(">> recursive_binary_search time: ", format(end_time - start_time, ".4f"))

 

1. 음료수 얼려먹기

: 단순하게 0인 부분을 채우고, 채워진 0 부분이 몇 개인지 출력하면 되는 문제이다.

'''
input:
4 5 
00110
00011
11111
00000
output: 3

input:
5 7
1010101
0101010
1111000
0100111
0110000
output: 8

input:
4 5
10001
00000
00000
10001
output: 1
'''

1) BFS

from collections import deque
dpos = [(0, 1), (-1, 0), (0, -1), (1, 0)]

def __main__():
    N, M = map(int, input().split())

    ice_tray = [0 for _ in range(N)]
    for i in range(N):
        ice_tray[i] = list(map(int, input()))
    
    ''' another way
    ice_tray = []
    for i in range(N):
        ice_tray.append(list(map(int, input())))
    '''

    visit = [[0 for col in range(M)] for row in range(N)]
    cnt = 0
    que = deque()

    for i in range(N):
        for j in range(M):
            if visit[i][j] or ice_tray[i][j]:
                continue
            que.append((i, j))
            visit[i][j]=1
            cnt += 1
            while que:
                x, y = que.popleft()
                for dx,dy in dpos:
                    nx, ny = x + dx, y + dy
                    if nx < 0 or nx >= N or ny < 0 or ny >= M or visit[nx][ny]:
                        continue
                    visit[nx][ny] = 1

                    if ice_tray[nx][ny] == 0:
                      que.append((nx,ny))

    return  print("output >>", cnt) 

__main__()

나의 실수> 우선 input에서 input().split()을 사용하면 배열에 제대로 채워지지 않는다. (이건 왜 안 되는지 모르겠다..)

 

2) DFS

dpos = [(0,1),(1,0),(0,-1),(-1,0)]

def DFS(x, y):
    global N, M, ice_tray, visit

    for dx, dy in dpos:
        nx, ny = x + dx, y + dy
        if nx < 0 or nx >= N or ny < 0 or ny >= M or visit[nx][ny] == 1:
            continue

        visit[nx][ny] = 1
        if ice_tray[nx][ny] == 0:
            DFS(nx, ny)

전역변수로 사용하기 위해서 global 키워드를 사용했다.

def __main__():
    global N, M, ice_tray, visit

    N, M = map(int, input().split())

    ice_tray = [0 for _ in range(N)]
    for row in range(N):
        ice_tray[row] = list(map(int, input()))

    visit = [[0 for _ in range(M)] for _ in range(N)]

    cnt = 0
    for i in range(N):
        for j in range(M):
            if ice_tray[i][j] == 1 or visit[i][j] == 1:
                continue
            visit[i][j] = 1
            DFS(i,j)
            cnt += 1
    print("output >> ", cnt)
    return 0

__main__()

 

2. 미로 탈출
: 미로에서 탈출 가능한 최단 경로의 길이를 출력하면 된다.

''' 
input:
5 6
101010
111111
000001
111111
111111
output: 10

input:
5 10 
1001100011
1001110100
1111010111
1010010101
1110111101
output: 20
'''

1) BFS

: BFS는 너비 우선 탐색이기 때문에 찾은 값이 최소 거리일 수 밖에 없다.

from collections import deque
dpos = [(0, 1), (-1, 0), (0, -1), (1, 0)]

def BFS(x,y,r):
  global N, M, maze, visit
  visit[x][y]=1
  que = deque()
  que.append((x,y,r))
  while que:
      x,y,r = que.popleft()
      for dx,dy in dpos:
        nx, ny, nr = x + dx,y + dy, r+1
        if nx < 0 or nx >= N or ny < 0 or ny >= M or visit[nx][ny]:
          continue
        if nx == N-1 and ny == M-1:
          return nr

        visit[nx][ny] = 1
        if maze[nx][ny] == 1:
          que.append((nx, ny, nr))
def __main__():
    global N, M, maze, visit

    N, M = map(int, input().split())
    maze = [0 for _ in range(N)]
    for i in range(N):
        maze[i] = list(map(int, input()))
    visit = [[0 for col in range(M)] for row in range(N)]

    return print("output >>", BFS(0, 0, 1))
  
__main__()

2) DFS

: DFS는 깊이 우선 탐색이므로 출구에 도착했을 때 계산한 경로들 중에 제일 작은 값을 찾아서 출력해야한다.

dpos = [(0, 1), (-1, 0), (0, -1), (1, 0)]

def DFS(x,y,cnt):
  global N, M, mincnt
  if x == N-1 and y == M-1:
    if mincnt > cnt:
      mincnt = cnt
    return cnt

  for dx, dy in dpos:
    nx, ny = x + dx, y + dy
    if nx < 0 or nx >= N or y < 0 or ny >= M or arr[nx][ny] == 0 or visit[nx][ny] == 1:
      continue
    
    if arr[nx][ny] == 1:
      visit[nx][ny] = 1
      DFS(nx, ny, cnt+1)
      visit[nx][ny] = 0
def __main__():
    global N, M, mincnt, arr, visit

    N, M = map(int, input().split())
    mincnt = 201 * 201
    arr = []
    for i in range(N):
        arr.append(list(map(int, input())))

    visit = [[0 for col in range(M)] for row in range(N)]
    visit[0][0] = 1

    DFS(0, 0, 1)

    return print("output >>", mincnt)
  
__main__()

 

BFS와 DFS로 문제를 풀면서 부족했던 점은 조건을 기입할 때 visit과 arr을 사용하는 것에 혼동하는 경우가 있는 것 같다.

주의해야 할 것 같다!

 

github.com/katherine4191/This-is-a-coding-test/tree/master/CH5%20BFS%26DFS

 

katherine4191/This-is-a-coding-test

Contribute to katherine4191/This-is-a-coding-test development by creating an account on GitHub.

github.com

 

1. selection sort 선택 정렬

: 가장 작은 것을 선택해 차례대로 맨 앞의 데이터와 바꾸는 정렬 방법

def selection_sort(arr):
    n = len(arr)
    for i in range(n):
        min_idx = i
        for j in range(i+1,n):
            if arr[min_idx] > arr[j]:
                min_idx = j
        arr[i], arr[min_idx] = arr[min_idx], arr[i]
        #print("sorting ... ",arr)
    return arr
''' output
sorting ...  [0, 5, 9, 7, 3, 1, 6, 2, 4, 8]
sorting ...  [0, 1, 9, 7, 3, 5, 6, 2, 4, 8]
sorting ...  [0, 1, 2, 7, 3, 5, 6, 9, 4, 8]
sorting ...  [0, 1, 2, 3, 7, 5, 6, 9, 4, 8]
sorting ...  [0, 1, 2, 3, 4, 5, 6, 9, 7, 8]
sorting ...  [0, 1, 2, 3, 4, 5, 6, 9, 7, 8]
sorting ...  [0, 1, 2, 3, 4, 5, 6, 9, 7, 8]
sorting ...  [0, 1, 2, 3, 4, 5, 6, 7, 9, 8]
sorting ...  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
sorting ...  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
'''

 

2. Insertion sort 삽입 정렬

: 특정한 값을 적절한 위치에 삽입하는 정렬 방법(그 위치 앞까지는 이미 정렬되어 있음)

def insertion_sort(arr):
    n = len(arr)
    for i in range(1,n):
        for j in range(i-1, -1, -1):
            if arr[j] > arr[j+1]: # 해당 데이터의 위치 자동 갱신 가능
                arr[j], arr[j+1] = arr[j+1], arr[j]
            else: 
                break
        #print("sorting ... ",arr)          
    return arr 
''' output
sorting ...  [5, 7, 9, 0, 3, 1, 6, 2, 4, 8]
sorting ...  [5, 7, 9, 0, 3, 1, 6, 2, 4, 8]
sorting ...  [0, 5, 7, 9, 3, 1, 6, 2, 4, 8]
sorting ...  [0, 3, 5, 7, 9, 1, 6, 2, 4, 8]
sorting ...  [0, 1, 3, 5, 7, 9, 6, 2, 4, 8]
sorting ...  [0, 1, 3, 5, 6, 7, 9, 2, 4, 8]
sorting ...  [0, 1, 2, 3, 5, 6, 7, 9, 4, 8]
sorting ...  [0, 1, 2, 3, 4, 5, 6, 7, 9, 8]
sorting ...  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
'''

나의 실수> 

j for문에서 j와 i를 사용하여 값을 비교했는데 이때 i로 인해 업데이트되는 값을 따라가지 못했는데 j+1을 통해 자동으로 값이 업데이트 되는 것을 알 수 있었다. 

 

3. quick sort 퀵 정렬

: pivot을 사용하여 왼쪽부터 pivot보다 큰 데이터를 찾고, 오른쪽부터 pivot보다 작은 데이터를 찾아 큰 데이터와 작은 데이터를 찾아 위치를 서로 교환하는 정렬 방법

: 작은 데이터와 큰 데이터를 찾았을 때 index가 엇갈린 경우에는 pivot과 찾은 작은 데이터(right index)와 교체한다

def quick_sort(pivot, end, arr):
    if pivot >= end:
        return    
    
    left, right = pivot + 1, end
    
    while left <= right:
        while left <= end and arr[pivot] >= arr[left]:
            left += 1
        while right > pivot and arr[pivot] <= arr[right]:
            right -= 1

        if left > right: # 두 값이 엇갈린 경우 작은 데이터(right)와 pivot 교체
            arr[pivot], arr[right] = arr[right], arr[pivot]
            #print("sorting ... "arr)
        else:
            arr[left], arr[right] = arr[right], arr[left]

    # 현재는 arr[right]에 pivot 값이 존재, pivot은 배열의 앞 부분을 의미
    quick_sort(pivot, right - 1, arr) 
    quick_sort(right + 1, end, arr)
    return arr
''' pivot이 바뀌는 과정
sorting ... [0, 1, 2, 4, 3, 5, 6, 9, 7, 8]
sorting ... [0, 1, 2, 4, 3, 5, 6, 9, 7, 8]
sorting ... [0, 1, 2, 3, 4, 5, 6, 9, 7, 8]
sorting ... [0, 1, 2, 3, 4, 5, 6, 9, 7, 8]
sorting ... [0, 1, 2, 3, 4, 5, 6, 8, 7, 9]
sorting ... [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
sorting ... [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
'''

나의 실수> while left <= right:을 생각하지 못했다.

또 배열을 slicing해서 넘기는 바보 같은 생각을 했다... merge할 거 아니면 start와 end idx로 재귀에서 arr을 수정하면 되는데, idx잘 계산해놓고 arr[:right-1], arr[right+1:] 이런 걸 넘기다니 정말 내가 왜 이렇게 짰었는지 도통 모를 일이다.

 

4. counting sort 계수 정렬

: 별도의 리스트를 선언하고 그 안에 정렬에 대한 정보를 담아 정렬하는 방법

def counting_sort(arr):
    count_arr = [0 for _ in range(max(arr)+1)]

    for val in arr:
        count_arr[val] += 1
    
    arr = []
    for i in range(len(count_arr)):
        while count_arr[i]:
            arr.append(i)
            count_arr[i] -= 1
            
    return arr

 

main 함수

def main():
    unsorted = [5,7,9,0,3,1,6,2,4,8]
    print("selection_sort",selection_sort(unsorted))
    
    unsorted = [7,5,9,0,3,1,6,2,4,8]
    print("insertion_sort", insertion_sort(unsorted))

    unsorted = [7,5,9,0,3,1,6,2,4,8]
    print("quick_sort", quick_sort(0, len(unsorted)-1, unsorted))
    
    unsorted = [7,5,9,0,3,1,6,2,9,1,4,8,0,5,2]
    print("counting_sort", counting_sort(unsorted))

main()
''' output
selection_sort [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
insertion_sort [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
quick_sort [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
counting_sort [0, 0, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9]
'''
  선택 정렬 삽입 정렬 퀵 정렬 계수 정렬
시간 복잡도 O(N^2) O(N^2) O(logN) O(N+K)

 

해당 문제와 모범답안은 아래 포스트를 확인해주세요
masso.tistory.com/61?category=891816

 

[4일차] 이.코.테.다! Ch3 그리디 greedy

Ch3. 그리디 greedy 1. 당장 좋은 것만 선택하는 그리디 - 현재 상황에서 당장 좋은 것만을 골라 현재의 선택이 나중에 미칠 영향은 고려하지 않는 방법 - 보통 코딩 테스트에서 출제되는 유형은 창

masso.tistory.com

문제 및 code

problem1. 거스름돈

def problem1(): # 거스름돈
  # 입력: 1260, 답:6 -> 500*2 + 100*2 + 50*1 + 10*1 
  coins = [500, 100, 50, 10]
  n = int(input("problem1: "))
  cnt = 0

  for coin in coins:
    cnt += n // coin
    n -= coin * (n // coin) # n %= coin

  print(cnt) 

모범 답안과의 비교>
- 파이썬에서 //: 몫을 반환하는 연산자. %: 나머지를 반환하는 연산자임을 상기시키자

- n을 남은 금액으로 갱신할 때 coin 개수와 coin 가격을 곱해 빼는 방법을 사용했는데, 나머지 연산자를 사용해

n %= coin으로 작성했다면 더 간결하고 빠른 코드였을 것이다.

 

problem2. 큰 수의 법칙

def problem2(): # 큰 수의 법칙
  # 5 8 3
  # 2 4 5 4 6
  # 답: 46 -> 6*3 + 5*1 + 6*3 + 5*1 
  # 5 8 5
  # 2 4 5 4 6
  # 답:47 -> 6*5 + 5*1 + 6*2
  n, m, k = map(int,input("problem2: ").split())
  arr = list(map(int,input().split()))

  arr.sort(reverse=True)
  sum = 0
  while m > 0:
    if m > k:
      sum += arr[0]*k
      m -= k
    else:
      sum += arr[0]*m
      m = 0

    if m >= 1:
      sum += arr[1]
      m -=1

  print(sum)

모범 답안과의 비교>

- list를 어떻게 정렬하냐에 따라 다를 수 있는데, 모범 답안에서는 오름차순으로 정렬하여 [n-1],[n-2] 값으로 제일 큰 값과 그 다음으로 큰 값을 구하였다.

- 반복되는 수열의 길이는 (K+1)이고 M을 (K+1)로 나눈 몫이 수열이 반복되는 횟수가 된다.
  여기에 (K+1)*M은 first가 반복하는 횟수가 되고, M이 (K+1)로 나누어 떨어지지 않는 것을 고려했을 때,
  '가장 큰 수가 더해지는 횟수는 int(M/(K+1)) * K + M%(K+1)'

  따라서, 더한 값의 결과는 first * 구한 횟수 + second * (m - 구한 횟수)가 된다.

 

problem3. 숫자 카드 게임

  # 3 3
  # 3 1 2
  # 4 1 4
  # 2 2 2
  # 답: 2
  # 2 4
  # 7 3 1 8
  # 3 3 3 4
  # 답: 3
  n, m = map(int, input("problem3: ").split())
  cards = [list(map(int, input().split())) for _ in range(n)]
  # [[3, 1, 2], [4, 1, 4], [2, 2, 2]]
  # [[7, 3, 1, 8], [3, 3, 3, 4]]

  for i in range(n):
    cards[i].sort()
  cards.sort(reverse=True)
  print(cards[0][0])

 

모범 답안과의 비교>

- 행을 입력받을 때마다 해당 행의 min값을 찾고, 찾은 min값을 이전 min값과 비교하면서 더 큰 min값으로 result 변수를 갱신하는데 나는 그냥 sort를 박아버렸다지...?!

 

problem4. 1이 될 때까지

def problem4():  # 1이 될 때까지
  # 25 5
  # 답:5
  n, k = map(int, input("problem4: ").split())
  count = n % k + n // k
  print(count)

 

모범 답안과의 비교>

- n을 k로 나누고, 1을 빼고 나서 또 다시 k로 나눌 수 있는 경우가 생길 수 있는데, 빨리 풀고 싶은 마음에 그 경우를 고려하지 못해서 정말 간단한 코드가 나왔다...!- k로 나눌 수 있는 만큼 나누고
  1) n<k 라면 그만큼 1을 빼면 되기 때문에 break를 한다.
  2) n>=k 라면 한 번 k를 더 나누고 이에 맞게 갱신하고 반복한다!

 

main 함수

def __main__():
  problem1()
  problem2()
  problem3()
  problem4()

__main__()

 

Ch6. 정렬

1. 기준에 따라 데이터를 정렬

  1-1. 정렬 알고리즘 개요

         - '정렬'
           : 데이터를 특정한 기준(오름차순 또는 내림차순)에 따라서 순서대로 나열하는 것이다.
           : 정렬 알고리즘으로 데이터를 정렬하고 나면 이진 탐색이 가능해진다.

 

  1-2. 선택 정렬

         - 매번 '가장 작은 것'을 '선택'한다는 의미에서 선택 정렬이다.

         - 다른 알고리즘들보다 비효율적이지만 가장 작은 데이터를 찾는 경우가 많으므로 소스 코드에 익숙해질 필요가 있다.

         - 소스 코드

arr = [7, 5, 9, 0, 3, 1, 6, 2, 4, 8]

for i in range(len(arr)):
  min_idx = i
  for j in range(i+1, len(arr)):
    if arr[min_idx] > arr[j]:
      min_idx = j
  arr[i], arr[min_idx] = arr[min_idx], arr[i] #swap

print(arr)
#include <stdio.h>
using namespace std;

int n = 10;
int arr[10] = { 7, 5, 9, 0, 3, 1, 6, 2, 4, 8 };

void swap(int* a, int* b) {
	int temp = *a;
	*a = *b;
	*b = temp;
	return;
}

void select_sort(int* array) {
	for (int i = 0; i < n; i++) {
		int min_idx = i;
		for (int j = i + 1; j < n; j++) {
			if (array[min_idx] > array[j])		min_idx = j;
		}
		swap(&array[min_idx], &array[i]);
	}
}

int main() { // 선택 정렬
	select_sort(arr);
	for (int i = 0; i < n; i++) printf("%d ", arr[i]);
	printf("\n");
	return 0;
}

         - 시간 복잡도
           :  선택 정렬은 N-1번만큼 가장 작은 수를 찾아서 맨 앞으로 보내야 한다. 또한 매번 가장 작은 수를 찾기 위해 비교 연산
              을 수행해야한다. // N + (N - 1) + (N - 2) + .... + 2

              따라서 시간 복잡도 O(N^2)가 걸린다.

            

  1-3. 삽입 정렬

         - 특정한 데이터를 적절한 위치에 '삽입'한다는 의미이다. 적절한 위치에 삽입되기 전에 그 앞까지의 데이터는 이미 정렬되
           어 있다고 가정한다.

         - 소스 코드

arr = [7,5,9,0,3,1,6,2,4,8]

for i in range(1,len(arr)):
  for j in range(i, 0, -1):
    if arr[j] < arr[j-1]:
      arr[j], arr[j-1] = arr[j-1], arr[j];
    else:
      break;

print(arr)
#include <stdio.h>
using namespace std;

int n = 10;
int arr[10] = { 7, 5, 9, 0, 3, 1, 6, 2, 4, 8 };

void swap(int* a, int* b) {
	int temp = *a;
	*a = *b;
	*b = temp;
	return;
}

void insert_sort(int* array) {
	for (int i = 1; i < n; i++) {
		for (int j = i; j > 0; j--) {
			if (array[j - 1] > array[j])	swap(&array[j], &array[j - 1]);
			else break;
		}
	}
}

int main() { // 삽입 정렬
	insert_sort(arr);
	for (int i = 0; i < n; i++) printf("%d ", arr[i]);
	printf("\n");
	return 0;
}

         - 시간 복잡도

           : 선택 정렬과 마찬가지로 이중 for문이 사용되었기 때문에 O(N^2)의 시간 복잡도를 가진다. 그렇지만, 현재 리스트의
             데이터가 거의 정렬되어 있는 상태라면 best의 경우 O(N)의 시간 복잡도를 가진다.

             거의 정렬되어 있는 상태의 데이터가 들어온다면 다른 정렬 알고리즘들보다 삽입 정렬을 이용하는 것이 정답 확률을
             높일 수 있다.

 

  1-4. 퀵 정렬

         - 지금까지 배운 알고리즘들 중에 가장 많이 사용되는 알고리즘들이다. 비슷한 속도를 가지는 병합 정렬 알고리즘이 있지
            만 이 책에서는 나오지 않는다. 

         - 기준(pivot)을 설정한 다음 큰 수와 작은 수를 교환한 후 리스트를 반으로 나누는 방식으로 동작한다.

         - pivot을 설정하고 리스트를 분할하는 방법에 따라 여러 가지 방식으로 구분하는데 여기서는 가장 대표적인 분할 방식인
           호어 방식(hoare partition)을 기준으로 퀵 정렬을 설명한다.

           '호어 방식(hoare partition)'
            1) 리스트의 첫번째 데이터를 pivot으로 설정하고 왼쪽에서부터 pivot보다 큰 수를, 오른쪽에서부터 pivot보다 작은
                수를 작은 데이터를 찾아 찾은 두 데이터의 위치를 서로 교환해준다.

            2) 교환하여 pivot의 왼쪽에는 pivot보다 작은 데이터가 위치하고, pivot의 오른쪽에 있는 피벗보다 큰 데이터가 위치

                하게 되면(=partition or divide가 다 되면) 왼쪽 리스트와 오른쪽 리스트에서 각각 위 과정을 또 진행한다.

            3) 분할(divide) or 파티션(partition)된 현재 리스트의 원소가 1개일 때 퀵 정렬은 끝난다.

 

         - 소스 코드

arr = [5,7,9,0,3,1,6,2,4,8]

def quick_sort(arr, start, end):
  if start >= end: # 원소가 하나일 때
    return
  pivot = start
  left = pivot + 1
  right = end

  while left <= right:
    # pivot보다 큰 것을 찾을 때까지 idx 증가
    while left <= end and arr[left] <= arr[pivot]:
      left += 1
    # pivot보다 작은 것을 찾을 때까지 idx 감소
    while right > start and arr[right] >= arr[pivot]:
      right -= 1
    
    if left > right: # 5 8 7 6 4
      # 엇갈렸다면 작은 arr[right]와 arr[pivot]를 교체
      arr[right], arr[pivot] = arr[pivot], arr[right]
    else:
      # 엇갈리지 않았을 때 작은 데이터와 큰 데이터 교체
      arr[left], arr[right] = arr[right], arr[left]

  quick_sort(arr, start, right - 1)
  quick_sort(arr, right + 1, end)

quick_sort(arr, 0, len(arr)-1)
print(arr)

파이썬의 장점을 살린 퀵 정렬

arr = [5,7,9,0,3,1,6,2,4,8]

def quick_sort(arr):
  if len(arr) <= 1: return arr

  pivot = arr[0]
  tail = arr[1:] # pivot을 제외한 list
  
  left_side = [x for x in tail if x <= pivot]
  right_side = [x for x in tail if x > pivot]

  return quick_sort(left_side) + [pivot] + quick_sort(right_side)

sorted_arr = quick_sort(arr)
print(sorted_arr)
#include <stdio.h>
using namespace std;

int n = 10;
int arr[10] = { 7, 5, 9, 0, 3, 1, 6, 2, 4, 8 };

void swap(int* a, int* b) {
	int temp = *a;
	*a = *b;
	*b = temp;
	return;
}

void quick_sort(int* array, int start, int end) {
	if(start >= end) return;
	int pivot = start;
	int left = start + 1;
	int right = end;

	while(left <= right){
		// pivot보다 큰 데이터를 찾을 때까지 반복
		while(left <= end && array[left] <= array[pivot]) left++;
		// pivot보다 작은 데이터를 찾을 때까지 반복
		while(right > start && array[pivot] <= array[right]) right--;
		
		if(left > right) swap(&array[pivot], &array[right]);
		else swap(&array[left], &array[right]);
	}

	quick_sort(arr, start, right - 1);
	quick_sort(arr, right + 1, end);
}

int main() { // 퀵 정렬
	quick_sort(arr, 0, n-1);
	for (int i = 0; i < n; i++) printf("%d ", arr[i]);
	printf("\n");
	return 0;
}

         - 시간 복잡도

           : 퀵 정렬은 평균적으로 O(NlogN)의 시간 복잡도를 가진다. (증명이 필요한데 코딩테스트와 관련 없으므로 넘어간다)
             평균 시간 복잡도는O(NlogN)이지만, 최악의 경우(이미 데이터가 정렬되어 있는 경우) O(N^2)이다.

             실제로 정렬 라이브러리에는 피벗값을 설정하는 추가적인 로직이 있기에 최악의 경우에도 O(NlogN)를 보장한다.

 

  1-5. 계수 정렬

         - 특정한 조건이 부합할 때만 사용할 수 있지만 매우 빠른 정렬 알고리즘이다. 
           '데이터의 크기 범위가 제한되어 양의 정수 형태로 표현할 수 있을 때'만 사용할 수 있다.

            ex) 0 이상 100 이하인 성적 데이터

         - 계수 정렬보다 조금 느리지만 처리할 수 있는 정수의 크기는 더 큰 기수 정렬(radix sort)가 있다.

           계수 정렬과 기수 정렬은 현존하는 알고리즘 중에 제일 빠르다.

 

         - 소스 코드

arr = [7,5,9,0,3,1,6,2,9,1,4,8,0,5,2]

count = [0] * (max(arr)+1)

for i in range(len(arr)):
  count[arr[i]] += 1

for i in range(len(count)):
  for j in range(count[i]):
    print(i, end=' ') # 띄어쓰기를 구분으로 설정
#include <stdio.h>
#define MAX_VALUE 9

using namespace std;

int n = 15;
int arr[15] = { 7, 5, 9, 0, 3, 1, 6, 2, 9, 1, 4, 8, 0, 5, 2 };
int arrCnt[MAX_VALUE + 1];

int main() { // 계수 정렬
	for (int i = 0; i < n; i++) {
		arrCnt[arr[i]] += 1;
	}
	for (int i = 0; i <= MAX_VALUE; i++) {
		for (int j = 0; j < arrCnt[i]; j++) {
			printf("%d ", i);
		}
	}
	printf("\n");
	return 0;
}

         - 시간 복잡도

           : 모든 데이터가 양의 정수인 상황에서 데이터의 개수를 N, 데이터 중 최대값의 크기를 K라고 할 때,
             계수 정렬의 시간 복잡도는 O(N+K)이다.
             (앞에서부터 데이터를 확인하면서 적절한 인덱스의 값을 증가시킬 뿐만 아니라, 추후 인덱스에 해당하는 값들을
              확인할 때 데이터 중 최댓값의 크기만큼 반복을 수행해야 하기 때문)

 

         - 공간 복잡도

           : 리스트의 크기가 적절하고, 동일한 값을 가지는 데이터가 여러 개 등장할 때 적합하다. 왜냐하면 데이터가 0과 999,999
             단 두 개가 존재한다고 가정했을 때 리스트이 크기가 100만 개가 되도록 설정해야하기 때문이다.

           : 일반적인 코딩 테스트의 시스템 환경에서는 메모리 공간상의 제약과 입출력 시간 문제로 입력되는 데이터의 개수를     
             1000만개 이상으로 설정하기 어렵기 때문에 1000만 개 미만으로 출제될 것이다.

 

  1-6. 파이썬의 정렬 라이브러리

         - 파이썬은 기본 정렬 라이브러리인 sorted() 함수를 제공한다. 퀵 정렬과 방식이 비슷한 병합 정렬로 만들어졌는데,

           병합 정렬은 일반적으로 퀵 정렬보다 느리지만 최악의 경우에도 시간 복잡도 O(NlogN)을 보장한다는 특징이 있다.

         - sorted() 함수는 list나 dictionary 자료형 등을 입력받아 정렬된 결과를 리스트 자료형으로 반환한다.

 

         - 소스 코드

arr = [7,5,9,0,3,1,6,2,4,8]

# sorted() 함수 사용하기
result = sorted(arr)
print(result)

# 리스트 객체의 내장 함수 sort() 사용하기
arr.sort()
print(arr)

# sorted()나 sort()를 이용할 때
# key를 사용하여 정렬 할 수 있다
arr = [('바나나',2), ('사과',5),('당근',3)]

def setting(data):
  return data[1]

result = sorted(arr, key=setting)
print(result)
#include <stdio.h>
#include <algorithm>

using namespace std;

int n = 10;
int arr[10] = { 7, 5, 9, 0, 3, 1, 6, 2, 4, 8 };

int main() { 
	sort(arr, arr + n);
	for (int i = 0; i < n; i++) printf("%d ", arr[i]);
	printf("\n");
	return 0;
}

         - 시간 복잡도

           : 정확하게 병합 정렬과 삽입 정렬의 아이디어를 더한 하이브리드 방식의 정렬 알고리즘을 사용하기에 항상 최악의

             경우에도 O(NlogN)을 보장한다.

           : 따라서 문제를 풀 때 별도의 요구가 없다면 단순히 정렬해야하는 상황에는 기본 정렬 라이브러리를 사용하고,
             데이터 범위가 한정되어 있으며 더 빠르게 동작해야할 때는 계수 정렬을 사용하자.

           

  1-7. 코딩 테스트에서 정렬 알고리즘이 사용되는 경우
        1) 정렬 라이브러리로 풀 수 있는 문제
           : 단순히 정렬 기법을 알고 있는지 물어보는 문제

        2) 정렬 알고리즘의 원리에 대해서 물어보는 문제

           : 선택 정렬, 삽입 정렬, 퀵 정렬 등의 원리를 알고 있어야 풀 수 있는 문제

        3) 더 빠른 정렬이 필요한 문제

           : 퀵 정렬 기반의 정렬 기법으로 문제를 풀 수 없으며 계수 정렬 등 다른 정렬 알고리즘을 이용하거나 문제에서 기존에
             알려진 알고리즘의 구조적인 개선을 고쳐야 풀 수 있는 문제

 

 

2. 위에서 아래로

   - 문제

   - 문제 해결

     : 수의 개수가 500개 이하로 매우 적고, 모든 수는 1 이상 100,000 이하의 자연수로 어떤 정렬 알고리즘을 사용해도

       문제를 해결할 수 있다. 여기서는 가장 편한 파이썬의 기본 라이브러리를 이용하는 것이 효과적이다.

 

   - 소스 코드

n = int(input())

arr = []
for i in range(n):
  arr.append(int(input()))

arr = sorted(arr, reverse=True)

for i in arr:
  print(i,end=' ')
#include <stdio.h>
#include <algorithm>
#include <vector>
using namespace std;

int N = 0;
vector<int> v;

bool compare(int a, int b){
	return a > b;
}

int main() { 
	scanf("%d", &N);
	for (int i = 0; i < N; i++){
		int x = 0;
		scanf("%d", &x);
		v.push_back(x);
	}
	sort(v.begin(), v.end(), compare);

	for (int i = 0; i < N; i++) printf("%d ", v[i]);
	printf("\n");
	return 0;
}

 

3. 성적이 낮은 순서로 학생 출력하기

   - 문제

 

   - 문제 해결

    : 학생의 정보가 최대 100,000개까지 입력될 수 있으므로 최악의 경우 O(NlogN)을 보장하는 알고리즘을 이용하거나
      O(N)을 보장하는 계수 정렬을 이용하면 된다.

    : 그뿐만 아니라 입력되는 데이터는 학생의 이름과 정수지만 출력할 때는 학생의 이름만 출력하면 되기 때문에
      학생 정보(점수, 이름)으로 묶은 뒤에 점수를 기준으로 수행해야 한다. 따라서 이런 경우에도 -> 파이썬 기본 정렬 library

 

   - 소스 코드

n = int(input())

list_info = []
for i in range(n):
  data = input().split()
  list_info.append((data[0], int(data[1])))
  
def setting(student):
  return student[1]
list_info = sorted(list_info, key=setting)

for student in list_info:
  print(student[0], end=' ')
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;

class Student {
	public:
		string name;
		int score;
		Student(string name, int score) {
			this->name = name;
			this->score = score;
		}
		bool operator < (Student &other) {
			return this->score < other.score;
		}
};

int N;
vector<Student> v;

int main() { 
	cin >> N;
	// scanf로 문자열을 받으려면 char[30] str 이런 형식이어야 한다.
	for (int i = 0; i < N; i++){
		string name; 
		int score;
		cin >> name >> score;
		v.push_back(Student(name, score));
	}
	sort(v.begin(), v.end());

	for (int i = 0; i < N; i++) {
		cout << v[i].name << " ";
	}
	cout << endl;
	return 0;
}

4. 두 배열의 원소 교체

   - 문제

   - 문제 해결

    : 기본 아이디어는 매번 배열 A에서 가장 작은 원소를 골라서, 배열 B에서 가장 큰 원소와 교체를 하는 것이다.

      (단, 배열 A에서 가장 작은 원소가 배열 B에 있는 가장 큰 원소보다 작을 때에만 교체를 해야한다)

    : 배열 A의 원소를 오름차순으로 정렬하고, 배열 B의 원소를 내림차순으로 설정한다. 두 배열의 원소를 가장 첫 번째 인덱스
      부터 차례대로 비교하면서 A의 원소가 B의 원소보다 작을 때 교체를 수행한다.

    : 두 배열의 원소가 100,000개 까지 입력될 수 있으므로 O(NlogN)을 보장하는 정렬 알고리즘을 이용해야 한다.

 

   - 소스 코드

n, k = map(int,input().split())
arrA = list(map(int,input().split()))
arrB = list(map(int,input().split()))

arrA.sort()
arrB.sort(reverse=True)

for i in range(k):
  if arrA[i] < arrB[i]:
    arrA[i], arrB[i] = arrB[i], arrA[i]
  else: 
    break;

print(sum(arrA))
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

int N, K;
vector<int> a, b;

bool compare(int x, int y) {
	return x > y;
}

int main() { 
	cin >> N >> K;
    
    for (int i = 0; i < N; i++) {
        int x;
        cin >> x;
        a.push_back(x);
    }
    
    for (int i = 0; i < N; i++) {
        int x;
        cin >> x;
        b.push_back(x);
    }
   
    sort(a.begin(), a.end()); // 배열 A는 오름차순 정렬 수행
    sort(b.begin(), b.end(), compare); // 배열 B는 내림차순 정렬 수행

    for (int i = 0; i < K; i++) {
        if (a[i] < b[i]) swap(a[i], b[i]);
        else break;
    }
    
    long long result = 0;
    for (int i = 0; i < N; i++) {
        result += a[i];
    }
    cout << result << endl;
	return 0;
}

+ Recent posts