728x90

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

 

1213번: 팰린드롬 만들기

첫째 줄에 문제의 정답을 출력한다. 만약 불가능할 때는 "I'm Sorry Hansoo"를 출력한다. 정답이 여러 개일 경우에는 사전순으로 앞서는 것을 출력한다.

www.acmicpc.net

이번 문제는 카테고리가 그리디이긴 하지만, 딱히 그리디를 생각하지는 않았다.

그냥 단순히 정렬 후 같은 것들은 묶어주고, 만약 문자가 짝수개이면 하나의 문자도 혼자있으면 안되며 홀수인 경우에는 하나의 문자만 홀로 있어야 한다.

다른 거 수와 문자열의 길이만 비교해서 구현하면 쉬운 문제로 딱히 로직에 관련해서는 설명하지 않도록 하겠다.

 

import sys

input_str = sorted(list(sys.stdin.readline().strip()))

start = ""
mid = ""
end = ""

chance = 0 if len(input_str) % 2 == 0 else 1

cur_char = ""

for i in input_str:
    if cur_char == "":
        cur_char = i
    else:
        if cur_char == i:
            start = start + cur_char
            end = cur_char + end
            cur_char = ""
        else:
            chance -= 1
            mid = cur_char
            cur_char = i

if cur_char != "":
    mid = cur_char

if chance >= 0:
    print(start + mid + end)
else:
    print("I'm Sorry Hansoo")

'알고리즘 > 그리디' 카테고리의 다른 글

백준 1715 카드 정렬하기 (Python)  (1) 2024.01.31
백준 1946 신입사원 (Python)  (1) 2024.01.31
백준 16953 A → B (Python)  (1) 2024.01.31
백준 1931 회의실 배정 (Python)  (0) 2024.01.30
백준 11399 ATM (Python)  (0) 2024.01.30
728x90

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

 

11399번: ATM

첫째 줄에 사람의 수 N(1 ≤ N ≤ 1,000)이 주어진다. 둘째 줄에는 각 사람이 돈을 인출하는데 걸리는 시간 Pi가 주어진다. (1 ≤ Pi ≤ 1,000)

www.acmicpc.net

이번 주는 그리디 알고리즘이다.

그리디가 동적프로그래밍보다 쉽다고 생각한다.

그리디는 그냥 계속 최소 혹은 최대만 가져오면 된다고 생각하기 때문..

 

해당 문제도 그냥 정렬 후 가장 작은 값부터 차례대로 가져올 수 있도록 했다.

 

import sys

N = int(sys.stdin.readline().strip())

P = sorted(list(map(int, sys.stdin.readline().split())))

cur_time = 0
result = 0

for i in P:
    cur_time += i
    result += cur_time

print(result)
728x90

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

 

2293번: 동전 1

첫째 줄에 n, k가 주어진다. (1 ≤ n ≤ 100, 1 ≤ k ≤ 10,000) 다음 n개의 줄에는 각각의 동전의 가치가 주어진다. 동전의 가치는 100,000보다 작거나 같은 자연수이다.

www.acmicpc.net

별로 어렵다고 생각하지 않았지만, 풀다보니 굉장히 어려운 문제였다.

단순하게 배열로 쭉 만들어서 더하며 나가면 된다고 생각했는 데, 구성이 같은 것들을 제외하느라 시간을 오래쓰게 되었다.

 

이해가 잘 될 수 있도록 보기를 통해 분석해보도록 하자.

보기는 1, 2, 5이 3가지 동전으로 10을 채우는 경우를 구하는 것이다.

 

경우의 수를 모두 구해보자.

5 5                
5 2 2 1            
5 2 1 1 1          
5 1 1 1 1 1        
2 2 2 2 2          
2 2 2 2 1 1        
2 2 2 1 1 1 1      
2 2 1 1 1 1 1 1    
2 1 1 1 1 1 1 1 1  
1 1 1 1 1 1 1 1 1 1

 

이렇게 10가지의 경우가 나오게된다.

이렇게보면 나누기로도 구할 수 있을 거 같은데, 일단 당연히 DP로 풀어야 하기에 생각해보지 않았다.

 

경우의 수가 중복이 되면 안되기 때문에 각 코인은 한 번씩 코인의 개수만큼 반복해야 한다.

일단 코인을 입력받으면, 해당 코인의 가치만큼의 경우의 수가 +1 되는 것이다.

 

그리고 1부터 목표가치만큼 진행하며, 만약 m의 가치의 경우의 수가 3이라면 m+(코인의 가치)의 가치에 해당하는 경우의 수도 3 늘어나게 되는 것이다.

 

이렇게 반복문을 진행해가면 된다.

 

import sys

n, k = map(int, sys.stdin.readline().split())

value = [0 for i in range(k+1)]

for i in range(n):
    coin = int(sys.stdin.readline().strip())
    if coin <= k:
        value[coin] += 1
    for t in range(1, (k+1) - coin):
        value[t+coin] += value[t]

print(value[-1])
728x90

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

 

1932번: 정수 삼각형

첫째 줄에 삼각형의 크기 n(1 ≤ n ≤ 500)이 주어지고, 둘째 줄부터 n+1번째 줄까지 정수 삼각형이 주어진다.

www.acmicpc.net

일단 문제가 DP이기 때문에 보자마자 단계가 있는지 확인한다.

 

일단 바로 각 층으로 단계를 구별할 수 있을 것 같다.

그렇게 단계를 나누면, 각 단계별로 영향을 받는 것은 해당 단계의 전 단계이다.

전 단계를 보고 해당 단계의 최대값을 구하며 내려가면 될 것 같다.

 

import sys

n = int(sys.stdin.readline())

max_list = [0 for i in range(n)]

for i in range(n):
    level = list(map(int, sys.stdin.readline().split()))
    cur_max_list = list()

    for t in range(i+1):
        if t == 0:
            cur_max_list.append(max_list[0] + level[0])
        elif t == i:
            cur_max_list.append(max_list[-1] + level[i])
        else:
            cur_max_list.append(max(max_list[t-1], max_list[t]) + level[t])
    max_list = cur_max_list

print(max(max_list))

 

각 층의 첫 번째 칸과 마지막 칸은 경우가 그냥 하나 밖에 존재하지 않는다.

그냥 위의 칸 + 현재 칸이다.

해당 값은 그냥 바로 구해서 넣어줄 수 있도록 하고, 그 사이의 칸들은 윗 단계에서 내려올 수 있는 2가지 수 중에서 큰 값을 가져와 본인의 수와 더해 최대값을 구할 수 있도록 하였다.

728x90

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

 

1149번: RGB거리

첫째 줄에 집의 수 N(2 ≤ N ≤ 1,000)이 주어진다. 둘째 줄부터 N개의 줄에는 각 집을 빨강, 초록, 파랑으로 칠하는 비용이 1번 집부터 한 줄에 하나씩 주어진다. 집을 칠하는 비용은 1,000보다 작거나

www.acmicpc.net

이번주도 DP가 주제이다.

 

DP = 단계별로 값을 저장이기 때문에 문제를 보자마자

한 줄마다 각 단계를 어떻게 저장할 수 있을지를 먼저 생각해보았다.

 

일단 각 줄마다 영향을 받는 것은 해당 줄의 전 줄이다.

만약 3번째 줄을 본다면, 2번째 줄의 집에만 영향을 받고 1번째 줄의 집에 색에는 영향을 받지 않는다.

 

그럼 한 줄씩 진행해가며, 전 단계의 집을 살핀 후 각 색마다 만들 수 있는 최소 비용을 구한다.

그렇게 한 단계씩 DP를 진행해가며, 마지막에는 그 중에서 최소값을 구해 출력하게 된다.

import sys

N = int(sys.stdin.readline())

min_list = [0 for i in range(3)]

for i in range(N):
    color = list(map(int, sys.stdin.readline().split()))
    min_list = [
        min(min_list[1], min_list[2]) + color[0],
        min(min_list[0], min_list[2]) + color[1],
        min(min_list[0], min_list[1]) + color[2]
    ]

print(min(min_list))

 

728x90

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

 

9251번: LCS

LCS(Longest Common Subsequence, 최장 공통 부분 수열)문제는 두 수열이 주어졌을 때, 모두의 부분 수열이 되는 수열 중 가장 긴 것을 찾는 문제이다. 예를 들어, ACAYKP와 CAPCAK의 LCS는 ACAK가 된다.

www.acmicpc.net

옛날에 굉장히 고생했었던 LCS이다.

그 기억 때문에 이번 동적 프로그래밍에서 이번 문제를 뽑은 거 같기도 하다.

 

당연히 동적 프로그래밍답게 배열이 등장한다.

하지만 여기서는 기존과는 다르게 2차원 배열이다.

이 생각을 못해서 옛날에 굉장히 고생했었다.

    A C A Y K P
               
C              
A              
P              
C              
A              
K              

 

일단 이런 식의 배열을 생성한다.

 

구하는 방식은 ACAYKP로 문자열을 기준 잡고

C, CA, CAP, CAPC, CAPCA, CAPACK를 순서대로 비교해가며 가장 긴 공통 수열을 찾아가는 방식이다.

 

각 문자열의 가장 앞에는 빈 문자열을 둔 것이고, 당연히 빈 문자열이기 때문에 공통 수열의 길이는 0이다.

 

우선 C부터 비교를 해보자

    A C A Y K P
  0 0 0 0 0 0 0
C 0 0 1 1 1 1 1
A              
P              
C              
A              
K              

 

가장 긴 공통 수열은 C 하나 일 것이다.

그러면 ACAYKP에서 C 이후로는 가장 긴 공통 수열의 길이가 1이 될 것이다.

 

그 후로는 A를 비교해본다.

현재 비교하는 문자열은 CA이다.

    A C A Y K P
  0 0 0 0 0 0 0
C 0 0 1 1 1 1 1
A 0 1 1 2 2 2 2
P              
C              
A              
K              

 

ACA 이후로는 CA가 겹치기 때문에 가장 긴 공통 수열의 길이는 2가 된다.

 

마지막으로 CAP까지만 비교해보자.

    A C A Y K P
  0 0 0 0 0 0 0
C 0 0 1 1 1 1 1
A 0 1 1 2 2 2 2
P 0 1 1 2 2 2 3
C              
A              
K              

 

CAP가 겹치기 때문에 길이는 3이 나오게 된다.

여기서 배열은

만약 해당 문자가 같을 경우(first_word[i] == second_word[t])에는 dp[i][t] = dp[i-1][t-1] + 1을 저장해주고

만약 해당 문자가 다른 경우(first_word[i] != second_word[t])에는 do[i][t] = max(dp[i][t-1], dp[i-1][t])로 저장해주면 된다.

 

그렇게 만들어진 최종 배열이다.

    A C A Y K P
  0 0 0 0 0 0 0
C 0 0 1 1 1 1 1
A 0 1 1 2 2 2 2
P 0 1 1 2 2 2 3
C 0 1 2 2 2 2 3
A 0 1 2 3 3 3 3
K 0 1 2 3 3 4 4

 

만들어진 코드는 다음과 같다.

import sys

first_word = list(sys.stdin.readline().strip())
second_word = list(sys.stdin.readline().strip())

max_length = max(len(first_word), len(second_word))

dp = [[0 for i in range(max_length + 1)] for t in range(max_length + 1)]

for i in range(len(first_word)):
    for t in range(len(second_word)):
        if first_word[i] == second_word[t]:
            dp[i][t] = dp[i-1][t-1] + 1
        else:
            dp[i][t] = max(dp[i][t-1], dp[i-1][t])


print(dp[len(first_word) - 1][len(second_word) - 1])
728x90

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

 

11053번: 가장 긴 증가하는 부분 수열

수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {10, 20, 10, 30, 20, 50} 인 경우에 가장 긴 증가하는 부분 수열은 A = {10, 20, 10, 30, 20, 50} 이

www.acmicpc.net

실버 2이지만 생각보다 오래걸렸다.

 

이 방법으로 푸는 게 아닌거라고는 알고 있지만, 어떻게든 풀어는 보았다.

 

DP인 만큼 일단 기억할 배열을 만든다.

result = [[None] for i in range(N)]

 

그리고 2중 반복문으로 해당 수로 만든 수열과 DP에 기억된 수열 중 최대 값이 본인보다 작은 수열 중에서 길이가 더 긴 수열로 본인 차례의 가장 긴 수열을 만들고, 마지막 결과에서 그 수열 중에서 길이가 가장 긴 수열의 길이를 출력하는 것이다.

 

import sys

N = int(input())

A = list(map(int, sys.stdin.readline().split()))

result = [[None] for i in range(N)]

for i in range(N):
    cur_list = [A[i]]
    for t in range(i):
        if A[i] > A[t]:
            if len(cur_list) < len(result[t]) + 1:
                cur_list = result[t] + [A[i]]
    result[i] = cur_list

result.sort(key= lambda x : len(x))

print(len(result[-1]))

 

 

'알고리즘 > 동적 프로그래밍' 카테고리의 다른 글

백준 1149 RGB거리 (Python)  (1) 2024.01.28
백준 9251 LCS (Python)  (0) 2024.01.20
백준 12865 평범한 배낭  (0) 2024.01.20
백준 11726 2xn 타일링  (0) 2024.01.17
백준 2193 이친수  (0) 2024.01.17
728x90

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

 

12865번: 평범한 배낭

첫 줄에 물품의 수 N(1 ≤ N ≤ 100)과 준서가 버틸 수 있는 무게 K(1 ≤ K ≤ 100,000)가 주어진다. 두 번째 줄부터 N개의 줄에 거쳐 각 물건의 무게 W(1 ≤ W ≤ 100,000)와 해당 물건의 가치 V(0 ≤ V ≤ 1,000)

www.acmicpc.net

DP에서 중요하다고 생각되는 내용 중 하나인 배낭 문제이다.

 

import sys

N, K = map(int, sys.stdin.readline().split())
dp = [[0] * (K + 1) for i in range(N + 1)]
thing = [[0, 0]]

for i in range(N):
    thing.append(list(map(int, sys.stdin.readline().split())))

for i in range(1, N + 1):
    w = thing[i][0]
    v = thing[i][1]
    for t in range(1, K + 1):
        if t < w:
            dp[i][t] = max(dp[i-1][t], dp[i][t-1])
        else:
            dp[i][t] = max(dp[i-1][t], dp[i][t-1],dp[i-1][t-w] + v)



print(dp[-1][-1])

옛날에 풀었던 코드이다.

 

너무 오래 전이라 기억도 안나고, 이번 주 과제이기 때문에 다시 풀어보도록 했다.

 

우선 DP 답게 기억할 배열이 필요하다.

범위는 무게인 0부터 W까지이다.

 

이제는 후보들을 반복문으로 돌면서 넣을지 말지를 판단해야 한다.

해당 짐이 들어갈 수 있는 모든 구간에 대해서, 현재 넣을 후보와 이 짐을 넣었을 때의 가치를 비교하고 무엇을 넣을지를 결정하면 된다.

import sys

N, K = map(int, sys.stdin.readline().split())

candidate_weight = []
candidate_value = []

for i in range(N):
    W, V = map(int, sys.stdin.readline().split())
    candidate_weight.append(W)
    candidate_value.append(V)

result = [0 for i in range(K + 1)]

for i in range(N):
    for t in range(candidate_weight[i], K + 1):
        result[t] = max(result[t], result[t-candidate_weight[i]] + candidate_value[i])

print(result[-1])

그래서 당연히 아무 생각없이 이렇게 코드를 만들었었다.

 

당연히 에러가 발생한다.

만약 가능한 무게가 8이고 4인 짐을 넣으려고 하면

[0, 0, 0, 0, 4, 0, 0, 0, 8] 이렇게 하나의 짐이 2번 들어가기 때문에 안쪽에 있는 반복문은 증가하는 방향이 아니라 감소하는 방향으로 만들어주어야 한다.

 

import sys

N, K = map(int, sys.stdin.readline().split())

candidate_weight = []
candidate_value = []

for i in range(N):
    W, V = map(int, sys.stdin.readline().split())
    candidate_weight.append(W)
    candidate_value.append(V)

result = [0 for i in range(K + 1)]

for i in range(N):
    for t in range(K, candidate_weight[i] - 1, -1):
        result[t] = max(result[t], result[t-candidate_weight[i]] + candidate_value[i])

print(max(result))

'알고리즘 > 동적 프로그래밍' 카테고리의 다른 글

백준 9251 LCS (Python)  (0) 2024.01.20
백준 11053 가장 긴 증가하는 부분 수열  (0) 2024.01.20
백준 11726 2xn 타일링  (0) 2024.01.17
백준 2193 이친수  (0) 2024.01.17
백준 2839 설탕 배달  (0) 2024.01.16

+ Recent posts