728x90

문제

APC에 온 것을 환영한다. 만약 여러분이 학교에서 자료구조를 수강했다면 해시 함수에 대해 배웠을 것이다. 해시 함수란 임의의 길이의 입력을 받아서 고정된 길이의 출력을 내보내는 함수로 정의한다. 해시 함수는 무궁무진한 응용 분야를 갖는데, 대표적으로 자료의 저장과 탐색에 쓰인다.

이 문제에서는 여러분이 앞으로 유용하게 쓸 수 있는 해시 함수를 하나 가르쳐주고자 한다. 먼저, 편의상 입력으로 들어오는 문자열에는 영문 소문자(a, b, ..., z)로만 구성되어있다고 가정하자. 영어에는 총 26개의 알파벳이 존재하므로 a에는 1, b에는 2, c에는 3, ..., z에는 26으로 고유한 번호를 부여할 수 있다. 결과적으로 우리는 하나의 문자열을 수열로 변환할 수 있다. 예를 들어서 문자열 "abba"은 수열 1, 2, 2, 1로 나타낼 수 있다.

해시 값을 계산하기 위해서 우리는 문자열 혹은 수열을 하나의 정수로 치환하려고 한다. 간단하게는 수열의 값을 모두 더할 수도 있다. 해시 함수의 정의에서 유한한 범위의 출력을 가져야 한다고 했으니까 적당히 큰 수 M으로 나눠주자. 짜잔! 해시 함수가 완성되었다. 이를 수식으로 표현하면 아래와 같다.

 H=∑i=0l−1aimodM

해시 함수의 입력으로 들어올 수 있는 문자열의 종류는 무한하지만 출력 범위는 정해져있다. 다들 비둘기 집의 원리에 대해서는 한 번쯤 들어봤을 것이다. 그 원리에 의하면 서로 다른 문자열이더라도 동일한 해시 값을 가질 수 있다. 이를 해시 충돌이라고 하는데, 좋은 해시 함수는 최대한 충돌이 적게 일어나야 한다. 위에서 정의한 해시 함수는 알파벳의 순서만 바꿔도 충돌이 일어나기 때문에 나쁜 해시 함수이다. 그러니까 조금 더 개선해보자.

어떻게 하면 순서가 달라졌을때 출력값도 달라지게 할 수 있을까? 머리를 굴리면 수열의 각 항마다 고유한 계수를 부여하면 된다는 아이디어를 생각해볼 수 있다. 가장 대표적인 방법은 항의 번호에 해당하는 만큼 특정한 숫자를 거듭제곱해서 곱해준 다음 더하는 것이 있다. 이를 수식으로 표현하면 아래와 같다.

 H=∑i=0l−1airimodM

보통 r과 M은 서로소인 숫자로 정하는 것이 일반적이다. 우리가 직접 정하라고 하면 힘들테니까 r의 값은 26보다 큰 소수인 31로 하고 M의 값은 1234567891(놀랍게도 소수이다!!)로 하자.

이제 여러분이 할 일은 위 식을 통해 주어진 문자열의 해시 값을 계산하는 것이다. 그리고 이 함수는 간단해 보여도 자주 쓰이니까 기억해뒀다가 잘 써먹도록 하자.

입력

첫 줄에는 문자열의 길이 L이 들어온다. 둘째 줄에는 영문 소문자로만 이루어진 문자열이 들어온다.

입력으로 주어지는 문자열은 모두 알파벳 소문자로만 구성되어 있다.

출력

문제에서 주어진 해시함수와 입력으로 주어진 문자열을 사용해 계산한 해시 값을 정수로 출력한다.

Small (50점)

  • 1 ≤ L ≤ 5

Large (50점)

  • 1 ≤ L ≤ 50

풀이 1

import sys
n = int(sys.stdin.readline())
s = sys.stdin.readline()
hashmap = {'a':1, 'b':2, 'c':3, 'd':4, 'e':5, 'f':6, 'g':7, 'h':8, 'i':9, 'j':10,
           'k':11, 'l':12, 'm':13,'n':14,'o':15,'p':16,'q':17,'r':18,'s':19
              ,'t':20,'u':21,'v':22,'w':23,'x':24,'y':25,'z':26
           }
result = 0
for i in range(n):
    result += hashmap[s[i]] * 31**i
print(result % 1234567891)

풀이 2

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

result = 0
for i in range(n):
    result += (ord(s[i])-96) * 31**i
print(result % 1234567891)
728x90

문제

로봇 청소기가 주어졌을 때, 청소하는 영역의 개수를 구하는 프로그램을 작성하시오.

로봇 청소기가 있는 장소는 N×M 크기의 직사각형으로 나타낼 수 있으며, 1×1크기의 정사각형 칸으로 나누어져 있다. 각각의 칸은 벽 또는 빈 칸이다. 청소기는 바라보는 방향이 있으며, 이 방향은 동, 서, 남, 북중 하나이다. 지도의 각 칸은 (r, c)로 나타낼 수 있고, r은 북쪽으로부터 떨어진 칸의 개수, c는 서쪽으로 부터 떨어진 칸의 개수이다.

로봇 청소기는 다음과 같이 작동한다.

  1. 현재 위치를 청소한다.
  2. 현재 위치에서 현재 방향을 기준으로 왼쪽 방향부터 차례대로 인접한 칸을 탐색한다.
    1. 왼쪽 방향에 아직 청소하지 않은 공간이 존재한다면, 그 방향으로 회전한 다음 한 칸을 전진하고 1번부터 진행한다.
    2. 왼쪽 방향에 청소할 공간이 없다면, 그 방향으로 회전하고 2번으로 돌아간다.
    3. 네 방향 모두 청소가 이미 되어있거나 벽인 경우에는, 바라보는 방향을 유지한 채로 한 칸 후진을 하고 2번으로 돌아간다.
    4. 네 방향 모두 청소가 이미 되어있거나 벽이면서, 뒤쪽 방향이 벽이라 후진도 할 수 없는 경우에는 작동을 멈춘다.

로봇 청소기는 이미 청소되어있는 칸을 또 청소하지 않으며, 벽을 통과할 수 없다.

입력

첫째 줄에 세로 크기 N과 가로 크기 M이 주어진다. (3 ≤ N, M ≤ 50)

둘째 줄에 로봇 청소기가 있는 칸의 좌표 (r, c)와 바라보는 방향 d가 주어진다. d가 0인 경우에는 북쪽을, 1인 경우에는 동쪽을, 2인 경우에는 남쪽을, 3인 경우에는 서쪽을 바라보고 있는 것이다.

셋째 줄부터 N개의 줄에 장소의 상태가 북쪽부터 남쪽 순서대로, 각 줄은 서쪽부터 동쪽 순서대로 주어진다. 빈 칸은 0, 벽은 1로 주어진다. 지도의 첫 행, 마지막 행, 첫 열, 마지막 열에 있는 모든 칸은 벽이다.

로봇 청소기가 있는 칸의 상태는 항상 빈 칸이다.

출력

로봇 청소기가 청소하는 칸의 개수를 출력한다.

코드

# 14503
import sys
from collections import deque
dx = [-1, 0, 1, 0]
dy = [0, 1, 0, -1]
n, m = map(int,sys.stdin.readline().split())
r, c, d = map(int,sys.stdin.readline().split())
arr = []
for _ in range(n):
    arr.append(list(map(int,sys.stdin.readline().split())))

def rotate_left(d): # 현재 바라보고있는 방향에서 왼쪽으로
    return (d+3) % 4

def back(d): # 후진
    return (d+2) % 4

def bfs(r,c,d):
    q = deque()
    q.append([r, c, d])
    arr[r][c] = 2 # 청소
    cnt = 1
    while q:
        qr, qc, qd = q.popleft()
        tmp_qd = qd
        for i in range(4):
            tmp_qd = rotate_left(tmp_qd) # 현재 방향에서 왼쪽으로
            nr = qr + dx[tmp_qd]
            nc = qc + dy[tmp_qd]
            # 왼쪽에 청소안한 방이 있다면
            if 0<=nr<n and 0<=nc<m and arr[nr][nc] == 0:
                # 아직 청소 안했으면 청소하고 방향바꾸기
                arr[nr][nc] = 2
                cnt += 1
                q.append([nr,nc,tmp_qd])
                break

            # 갈 곳이 없다면
            elif i==3:
                # 방향 유지한채 한칸 후진
                nr = qr + dx[back(qd)]
                nc = qc + dy[back(qd)]
                q.append([nr, nc, qd])

                if arr[nr][nc] == 1: # 뒤에도 벽이 있다면 중단
                    return cnt

print(bfs(r,c,d))

풀이

bfs로 풀이했다.

dx, dy 방향 배열을 북, 동 , 남, 서 (0, 1, 2, 3) 으로 저장해둔다.

왼쪽으로 이동하는 함수와 후진하는 함수를 구현해뒀다.

728x90

문제

총 8개의 톱니를 가지고 있는 톱니바퀴 4개가 아래 그림과 같이 일렬로 놓여져 있다. 또, 톱니는 N극 또는 S극 중 하나를 나타내고 있다. 톱니바퀴에는 번호가 매겨져 있는데, 가장 왼쪽 톱니바퀴가 1번, 그 오른쪽은 2번, 그 오른쪽은 3번, 가장 오른쪽 톱니바퀴는 4번이다.

이때, 톱니바퀴를 총 K번 회전시키려고 한다. 톱니바퀴의 회전은 한 칸을 기준으로 한다. 회전은 시계 방향과 반시계 방향이 있고, 아래 그림과 같이 회전한다.

톱니바퀴를 회전시키려면, 회전시킬 톱니바퀴와 회전시킬 방향을 결정해야 한다. 톱니바퀴가 회전할 때, 서로 맞닿은 극에 따라서 옆에 있는 톱니바퀴를 회전시킬 수도 있고, 회전시키지 않을 수도 있다. 톱니바퀴 A를 회전할 때, 그 옆에 있는 톱니바퀴 B와 서로 맞닿은 톱니의 극이 다르다면, B는 A가 회전한 방향과 반대방향으로 회전하게 된다. 예를 들어, 아래와 같은 경우를 살펴보자.

두 톱니바퀴의 맞닿은 부분은 초록색 점선으로 묶여있는 부분이다. 여기서, 3번 톱니바퀴를 반시계 방향으로 회전했다면, 4번 톱니바퀴는 시계 방향으로 회전하게 된다. 2번 톱니바퀴는 맞닿은 부분이 S극으로 서로 같기 때문에, 회전하지 않게 되고, 1번 톱니바퀴는 2번이 회전하지 않았기 때문에, 회전하지 않게 된다. 따라서, 아래 그림과 같은 모양을 만들게 된다.

위와 같은 상태에서 1번 톱니바퀴를 시계 방향으로 회전시키면, 2번 톱니바퀴가 반시계 방향으로 회전하게 되고, 2번이 회전하기 때문에, 3번도 동시에 시계 방향으로 회전하게 된다. 4번은 3번이 회전하지만, 맞닿은 극이 같기 때문에 회전하지 않는다. 따라서, 아래와 같은 상태가 된다.

톱니바퀴의 초기 상태와 톱니바퀴를 회전시킨 방법이 주어졌을 때, 최종 톱니바퀴의 상태를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 1번 톱니바퀴의 상태, 둘째 줄에 2번 톱니바퀴의 상태, 셋째 줄에 3번 톱니바퀴의 상태, 넷째 줄에 4번 톱니바퀴의 상태가 주어진다. 상태는 8개의 정수로 이루어져 있고, 12시방향부터 시계방향 순서대로 주어진다. N극은 0, S극은 1로 나타나있다.

다섯째 줄에는 회전 횟수 K(1 ≤ K ≤ 100)가 주어진다. 다음 K개 줄에는 회전시킨 방법이 순서대로 주어진다. 각 방법은 두 개의 정수로 이루어져 있고, 첫 번째 정수는 회전시킨 톱니바퀴의 번호, 두 번째 정수는 방향이다. 방향이 1인 경우는 시계 방향이고, -1인 경우는 반시계 방향이다.

출력

총 K번 회전시킨 이후에 네 톱니바퀴의 점수의 합을 출력한다. 점수란 다음과 같이 계산한다.

  • 1번 톱니바퀴의 12시방향이 N극이면 0점, S극이면 1점
  • 2번 톱니바퀴의 12시방향이 N극이면 0점, S극이면 2점
  • 3번 톱니바퀴의 12시방향이 N극이면 0점, S극이면 4점
  • 4번 톱니바퀴의 12시방향이 N극이면 0점, S극이면 8점

코드

# 14891
import sys
def rotate(gearnum, clockwise):
    isRotate = [False for _ in range(4)] # 각 톱니바퀴 회전 가능한지
    isDirection = [0 for _ in range(4)] # 각 톱니바퀴 방향
    isRotate[gearnum] = True
    isDirection[gearnum] = clockwise

    tempn = gearnum
    tempd = clockwise
    # gear[gearnum][6] 과 gearnum 전 톱니바퀴들의 [2]비교
    for i in range(gearnum-1,-1,-1):
        if gear[tempn][6] != gear[i][2]:
            # 다르면 반대방향으로 움직여야함
            tempd = -tempd # 새롭게 복사
            isDirection[i] = tempd
            tempn = i # 새롭게 복사
            isRotate[i] = True
        else: # 같으면 안움직여도됨
            break

    tempn = gearnum
    tempd = clockwise
    # gear[gearnum][2] 과 gearnum 후 톱니바퀴들의 [6]비교
    for i in range(gearnum+1, 4):
        if gear[tempn][2] != gear[i][6]:
            # 다르면 반대방향으로 움직여야함
            tempd = -tempd
            isDirection[i] = tempd
            tempn = i
            isRotate[i] = True
        else: # 같으면 안움직여도됨
            break

    # 톱니바퀴 회전
    for i in range(4):
        if isRotate[i]: # 회전 가능한 것만
            if isDirection[i] == 1: # 시계방향 회전
                # 마지막 pop해서 배열 처음에 붙이기
                tmp = gear[i].pop()
                gear[i] = [tmp] + gear[i]
            else: # 반시계방향 회전
                # 처음꺼 뽑아서 마지막에 붙이기
                tmp = gear[i][0]
                del gear[i][0]
                gear[i].append(tmp)

gear = []
for i in range(4):
    gear.append(list(sys.stdin.readline().rstrip()))
k = int(sys.stdin.readline()) #회전 횟수
for _ in range(k):
    # 톱니바퀴번호, 방향
    gear_num, clock_wise = map(int,sys.stdin.readline().split())
    rotate(gear_num-1, clock_wise)
result = 0
for i in range(4):
#     print(gear[i])
    if gear[i][0] == '1': # S극이면
        result += 2 ** i
print(result)

풀이

톱니바퀴가 맞물리는 상황에서 극이 같으면 안움직이고 다르면 반대방향으로 움직인다.

이를 봤을 때, 만약 2번톱니바퀴를 시계반대방향으로 움직이면

  1. gear[2][6] 과 gear[i][2]를 비교 (단, i 는 2번 톱니바퀴보다 작은 것들 중)
  2. gear[2][2] 과 gear[i][6]를 비교 (단, i 는 2번 톱니바퀴보다 큰 것들 중)

비교해서 같으면 break

다르면 회전할 수 있는지와 방향 표시

여기서 또 움직인 톱니바퀴 번호와 방향을 계속 기록해야 다음꺼에 영향을 줄 수 있다.

그래서 1 과 2 전에 tempn, tempd 변수에 기록해둔다.

 


회전

  • 시계방향 - 마지막 원소 뽑아서 맨 처음에 삽입
  • 반시계방향 - 처음 원소 뽑아서 맨 마지막에 삽입

 

728x90

문제

10진법 수 N이 주어진다. 이 수를 B진법으로 바꿔 출력하는 프로그램을 작성하시오.

10진법을 넘어가는 진법은 숫자로 표시할 수 없는 자리가 있다. 이런 경우에는 다음과 같이 알파벳 대문자를 사용한다.

A: 10, B: 11, ..., F: 15, ..., Y: 34, Z: 35

입력

첫째 줄에 N과 B가 주어진다. (2 ≤ B ≤ 36) N은 10억보다 작거나 같은 자연수이다.

출력

첫째 줄에 10진법 수 N을 B진법으로 출력한다.

코드

#include <iostream>
#include <algorithm>
using namespace std;

int main(){
    int n, b;
    cin >> n >> b;

    string b_num;
    while (n!=0){
        int tmp = n % b;
        if (tmp > 9){
            tmp = tmp - 10 + 'A';
            b_num += tmp;
        }
        else{
            b_num += ('0' + tmp);

        }
        n/=b;
    }
    reverse(b_num.begin(), b_num.end());
    cout << b_num << '\n';
    return 0;
}

풀이

이번주에 코테 잡힌게 python이 안되어서 망했다.

그래도 문법 기억해내면서 풀어보는중 ㅠㅠ

10미만까지는 그냥 수를 진법으로 나눈 자리 수를 리턴

10이상부터는 -10 하고 'A' 부터 시작..

최종적으로 거꾸로 출력해야한다.

728x90

문제

n(2 ≤ n ≤ 100)개의 도시가 있다. 그리고 한 도시에서 출발하여 다른 도시에 도착하는 m(1 ≤ m ≤ 100,000)개의 버스가 있다. 각 버스는 한 번 사용할 때 필요한 비용이 있다.

모든 도시의 쌍 (A, B)에 대해서 도시 A에서 B로 가는데 필요한 비용의 최솟값을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 도시의 개수 n이 주어지고 둘째 줄에는 버스의 개수 m이 주어진다. 그리고 셋째 줄부터 m+2줄까지 다음과 같은 버스의 정보가 주어진다. 먼저 처음에는 그 버스의 출발 도시의 번호가 주어진다. 버스의 정보는 버스의 시작 도시 a, 도착 도시 b, 한 번 타는데 필요한 비용 c로 이루어져 있다. 시작 도시와 도착 도시가 같은 경우는 없다. 비용은 100,000보다 작거나 같은 자연수이다.

시작 도시와 도착 도시를 연결하는 노선은 하나가 아닐 수 있다.

출력

n개의 줄을 출력해야 한다. i번째 줄에 출력하는 j번째 숫자는 도시 i에서 j로 가는데 필요한 최소 비용이다. 만약, i에서 j로 갈 수 없는 경우에는 그 자리에 0을 출력한다.

코드

# 11401
import sys
INF = int(1e9)
n = int(sys.stdin.readline()) # 도시 (노드)
m = int(sys.stdin.readline()) # 버스 (간선)
graph = [[INF]*(n+1) for _ in range(n+1)] # 최단경로 저장

# 자기 자신 지나는 경로 0으로 초기화
for a in range(1,n+1):
    for b in range(1,n+1):
        if a==b:
            graph[a][b] = 0

for _ in range(m):
    a, b, c = map(int,sys.stdin.readline().split())
    # 시작 도시와 도착 도시를 연결하는 노선은 하나가 아닐 수 있다.
    # graph[a][b] = c
    graph[a][b] = min(graph[a][b],c)

# 플로이드 워셜 수행
for k in range(1, n+1):
    for a in range(1, n+1):
        for b in range(1, n+1):
            graph[a][b] = min(graph[a][b], graph[a][k]+graph[k][b])

# 최단경로 출력
for a in range(1, n+1):
    for b in range(1, n+1):
        if graph[a][b] == INF: # 갈 수 없다면 0으로 출력
            print(0, end=" ")
        else:
            print(graph[a][b], end=" ")
    print()

풀이

시작 도시와 도착 도시를 연결하는 노선이 하나가 아닐 수 있다는 것에 주의해서

플로이드 워셜 알고리즘을 수행하면 된다.

728x90

문제

인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다.

연구소는 크기가 N×M인 직사각형으로 나타낼 수 있으며, 직사각형은 1×1 크기의 정사각형으로 나누어져 있다. 연구소는 빈 칸, 벽으로 이루어져 있으며, 벽은 칸 하나를 가득 차지한다. 

일부 칸은 바이러스가 존재하며, 이 바이러스는 상하좌우로 인접한 빈 칸으로 모두 퍼져나갈 수 있다. 새로 세울 수 있는 벽의 개수는 3개이며, 꼭 3개를 세워야 한다.

예를 들어, 아래와 같이 연구소가 생긴 경우를 살펴보자.

2 0 0 0 1 1 0
0 0 1 0 1 2 0
0 1 1 0 1 0 0
0 1 0 0 0 0 0
0 0 0 0 0 1 1
0 1 0 0 0 0 0
0 1 0 0 0 0 0

이때, 0은 빈 칸, 1은 벽, 2는 바이러스가 있는 곳이다. 아무런 벽을 세우지 않는다면, 바이러스는 모든 빈 칸으로 퍼져나갈 수 있다.

2행 1열, 1행 2열, 4행 6열에 벽을 세운다면 지도의 모양은 아래와 같아지게 된다.

2 1 0 0 1 1 0
1 0 1 0 1 2 0
0 1 1 0 1 0 0
0 1 0 0 0 1 0
0 0 0 0 0 1 1
0 1 0 0 0 0 0
0 1 0 0 0 0 0

바이러스가 퍼진 뒤의 모습은 아래와 같아진다.

2 1 0 0 1 1 2
1 0 1 0 1 2 2
0 1 1 0 1 2 2
0 1 0 0 0 1 2
0 0 0 0 0 1 1
0 1 0 0 0 0 0
0 1 0 0 0 0 0

벽을 3개 세운 뒤, 바이러스가 퍼질 수 없는 곳을 안전 영역이라고 한다. 위의 지도에서 안전 영역의 크기는 27이다.

연구소의 지도가 주어졌을 때 얻을 수 있는 안전 영역 크기의 최댓값을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. (3 ≤ N, M ≤ 8)

둘째 줄부터 N개의 줄에 지도의 모양이 주어진다. 0은 빈 칸, 1은 벽, 2는 바이러스가 있는 위치이다. 2의 개수는 2보다 크거나 같고, 10보다 작거나 같은 자연수이다.

빈 칸의 개수는 3개 이상이다.

출력

첫째 줄에 얻을 수 있는 안전 영역의 최대 크기를 출력한다.

코드

# 14502
import sys
from collections import deque
import copy

def bfs():
    q = deque()
    tmp_graph = copy.deepcopy(graph) # 벽 설치 그래프 깊은 복사
    for i in range(N):
        for j in range(M):
            if tmp_graph[i][j] == 2: # 바이러스 있는 좌표만 큐에 넣기
                q.append((i,j))
    while q:
        x, y = q.popleft()
        for i in range(4):
            nx = x + dx[i]
            ny = y + dy[i]
            if 0 <= nx < N and 0 <= ny < M and tmp_graph[nx][ny] == 0:
                tmp_graph[nx][ny] = 2
                q.append((nx, ny))
    # 안전영역 ( 0인 칸 세기 )
    cnt0 = 0
    global answer
    for i in range(N):
        cnt0 += tmp_graph[i].count(0)
    answer = max(answer, cnt0)


def newwall(cnt):
    if cnt == 3:
        bfs() #벽 3개 새롭게 만들면 bfs 진행
        return #재귀함수로 돌아가서 벽 없애고 다시 재귀함수 
    for i in range(N):
        for j in range(M):
            if graph[i][j] == 0:
                graph[i][j] = 1 # 빈 칸이면 벽 설치
                newwall(cnt+1) # 벽 설치 재귀함수
                graph[i][j] = 0

N, M = map(int,sys.stdin.readline().split())
graph = [list(map(int,sys.stdin.readline().split())) for _ in range(N)]
dx = [0, -1, 0, 1]
dy = [-1, 0, 1, 0]
answer = 0
newwall(0) #벽 만들기
print(answer)

풀이

바이러스(2) 주변에서 빈칸(0) 중 3개를 선택해 벽(1)을 세우고 bfs로 바이러스가 퍼질 때, 0의 개수 중 최댓값을 구한다.

벽을 3개 세우는 모든 경우의 수에 bfs를 실행시켜 최대값을 구해야 한다.

이때, 경우의 수마다 bfs 실행 시, copy 라이브러리의 deepcopy를 사용한다.

728x90

문제

이 문제는 아주 평범한 배낭에 관한 문제이다.

한 달 후면 국가의 부름을 받게 되는 준서는 여행을 가려고 한다. 세상과의 단절을 슬퍼하며 최대한 즐기기 위한 여행이기 때문에, 가지고 다닐 배낭 또한 최대한 가치 있게 싸려고 한다.

준서가 여행에 필요하다고 생각하는 N개의 물건이 있다. 각 물건은 무게 W와 가치 V를 가지는데, 해당 물건을 배낭에 넣어서 가면 준서가 V만큼 즐길 수 있다. 아직 행군을 해본 적이 없는 준서는 최대 K만큼의 무게만을 넣을 수 있는 배낭만 들고 다닐 수 있다. 준서가 최대한 즐거운 여행을 하기 위해 배낭에 넣을 수 있는 물건들의 가치의 최댓값을 알려주자.

입력

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

입력으로 주어지는 모든 수는 정수이다.

출력

한 줄에 배낭에 넣을 수 있는 물건들의 가치합의 최댓값을 출력한다.

코드

# 12865
# 냅색 알고리즘
import sys
n, k = map(int,sys.stdin.readline().split())
knapsack = [[0 for _ in range(k+1)] for _ in range(n+1)]
stuff = [[0,0]]
for _ in range(n):
    stuff.append(list(map(int,sys.stdin.readline().split())))

for i in range(n+1):
    for j in range(k+1):
        weight = stuff[i][0]
        value = stuff[i][1]

        if j < weight: # 버틸 수 있는 무게가 현재 무게보다 작다면 전에 있는 값 그대로
            knapsack[i][j] = knapsack[i-1][j]
        else:
            # max( 현재 물건을 넣어주고 남은 무게를 채울 수 있는 최대값 / 다른 물건들로 채우는 값 )
            knapsack[i][j] = max(value + knapsack[i-1][j-weight], knapsack[i-1][j])
print(knapsack[n][k])

풀이

냅색 알고리즘이다. 

 

현재 물건이 현재 돌고 있는 무게보다 작다면,

바로 [이전물건][같은무게] 값을 가져온다.

현재 물건이 현재 돌고 있는 무게보다 크다면,

max(현재 물건을 넣어주고 남은 무게를 채울 수 있는 최대값, 다른 물건으로 채우는 값) 을 knapsack[i][j]에 삽입한다.

 

최종적으로 knapsack[n][k] 출력하면 무게가 k일 때의 최대 값을 출력할 수 있다.

+ Recent posts