물건 n개와, 무게 합이 W까지 들어가는 배낭 하나가 있다.
i번 물건의 무게는 wᵢ이고 가치는 vᵢ이다.
물건마다 배낭에 넣거나 넣지 않거나 둘 중 하나만 고를 수 있다.
한 물건을 두 번 넣거나 쪼개서 넣을 수는 없다.
넣은 물건의 무게 합이 W 이하가 되도록 고를 때, 넣은 물건의 가치 합으로 가능한 가장 큰 값을 출력하라.
어떤 물건도 넣을 수 없으면 0을 출력한다.
입력
첫 줄에 n과 W, 다음 n줄에 줄마다 wᵢ와 vᵢ가 빈칸으로 나뉘어 주어진다. 1 ≤ n ≤ 100, 1 ≤ W ≤ 9,999, 1 ≤ wᵢ ≤ 10,000, 1 ≤ vᵢ ≤ 1,000,000
출력
가치 합의 가장 큰 값 하나
예시 1 · stdin
4 7
6 13
4 8
3 6
5 12
stdout
14
예시 2 · stdin
2 5
6 100
7 50
stdout
0
main.py
오류 목록00F8 ↓ · ⇧F8 ↑
입력(stdin)
실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).
$ 준비됐어요
코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.
다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이
무게당 가치가 큰 것부터 담는 욕심(greedy)은 틀려요: 예시 1에서 무게당 가치가 가장 큰 5짜리(12)를 먼저 넣으면 남은 2에 아무것도 못 넣어서 12지만, 답은 4 + 3으로 14예요. 그래서 동적 계획법(DP)을 써요. best[c] = 무게 합 c 이하로 얻는 가장 큰 가치로 두고, 물건마다 best[c] = max(best[c], best[c - w] + v)로 고쳐요. 표를 n × W 2차원으로 만들 필요는 없어요: 한 줄(W + 1칸)을 두고 c를 큰 쪽에서 작은 쪽으로 돌면 best[c - w]가 아직 이 물건을 넣기 전 값이에요. 작은 쪽부터 돌면 방금 이 물건을 넣은 값을 또 읽어서 한 물건을 여러 번 넣게 돼요(그건 개수 제한이 없는 배낭이에요) — 가장 흔한 함정이에요. 무게가 W보다 큰 물건은 반복이 한 번도 돌지 않아 저절로 빠져요. 가치 합은 많아야 1억이라 모든 언어의 32비트 정수에 들어가요. 한 줄 만들기: Python [0] * (W + 1), JavaScript·TypeScript new Array(W + 1).fill(0), Java new int[W + 1]·C# new int[W + 1](0으로 채워져 있어요), C++ std::vector<int>(W + 1, 0), C는 static int best[10000](static이라 0으로 시작하고 스택도 쓰지 않아요), 크레이콘 새목록(W + 1, 0) — 크레이콘 목록은 10,000칸까지라 W를 9,999까지로 두었어요. 거꾸로 도는 반복: Python range(W, w - 1, -1)(끝 w - 1은 들어가지 않아요), C 계열·크레이콘 c = W; c >= w; c--.
Pythonmain.py · 11 줄
n, W = map(int, input().split())# best[c] = 무게 합이 c 이하일 때 얻는 가장 큰 가치(지금까지 본 물건만 써서)best = [0] * (W + 1)for _ in range(n): w, v = map(int, input().split()) # 큰 무게부터 거꾸로: best[c - w]가 아직 '이 물건을 넣기 전' 값이라 한 번만 넣는다 for c in range(W, w - 1, -1): if best[c - w] + v > best[c]: best[c] = best[c - w] + vprint(best[W])
JavaScriptmain.js · 12 줄
const [n, W, ...rest] = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);// best[c] = 무게 합이 c 이하일 때 얻는 가장 큰 가치(지금까지 본 물건만 써서)const best = new Array(W + 1).fill(0);for (let i = 0; i < n; i++) { const w = rest[2 * i], v = rest[2 * i + 1]; // 큰 무게부터 거꾸로: best[c - w]가 아직 '이 물건을 넣기 전' 값이라 한 번만 넣는다 for (let c = W; c >= w; c--) { best[c] = Math.max(best[c], best[c - w] + v); }}console.log(best[W]);
TypeScriptmain.ts · 14 줄
import { readFileSync } from "node:fs";const [n, W, ...rest]: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);// best[c] = 무게 합이 c 이하일 때 얻는 가장 큰 가치(지금까지 본 물건만 써서)const best: number[] = new Array<number>(W + 1).fill(0);for (let i = 0; i < n; i++) { const w: number = rest[2 * i], v: number = rest[2 * i + 1]; // 큰 무게부터 거꾸로: best[c - w]가 아직 '이 물건을 넣기 전' 값이라 한 번만 넣는다 for (let c = W; c >= w; c--) { best[c] = Math.max(best[c], best[c - w] + v); }}console.log(best[W]);
JavaMain.java · 26 줄
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.StringTokenizer;public class Main { public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(in.readLine()); int n = Integer.parseInt(st.nextToken()); int W = Integer.parseInt(st.nextToken()); // best[c] = 무게 합이 c 이하일 때 얻는 가장 큰 가치(지금까지 본 물건만 써서) int[] best = new int[W + 1]; for (int i = 0; i < n; i++) { st = new StringTokenizer(in.readLine()); int w = Integer.parseInt(st.nextToken()); int v = Integer.parseInt(st.nextToken()); // 큰 무게부터 거꾸로: best[c - w]가 아직 '이 물건을 넣기 전' 값이라 한 번만 넣는다 for (int c = W; c >= w; c--) { best[c] = Math.max(best[c], best[c - w] + v); } } System.out.println(best[W]); }}
C#Main.cs · 26 줄
using System;class Program{ static void Main() { string[] first = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries); int n = int.Parse(first[0]); int W = int.Parse(first[1]); // best[c] = 무게 합이 c 이하일 때 얻는 가장 큰 가치(지금까지 본 물건만 써서) int[] best = new int[W + 1]; for (int i = 0; i < n; i++) { string[] item = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries); int w = int.Parse(item[0]); int v = int.Parse(item[1]); // 큰 무게부터 거꾸로: best[c - w]가 아직 '이 물건을 넣기 전' 값이라 한 번만 넣는다 for (int c = W; c >= w; c--) { best[c] = Math.Max(best[c], best[c - w] + v); } } Console.WriteLine(best[W]); }}
C++main.cpp · 20 줄
#include <algorithm>#include <iostream>#include <vector>int main() { int n, W; std::cin >> n >> W; // best[c] = 무게 합이 c 이하일 때 얻는 가장 큰 가치(지금까지 본 물건만 써서) std::vector<int> best(W + 1, 0); for (int i = 0; i < n; i++) { int w, v; std::cin >> w >> v; // 큰 무게부터 거꾸로: best[c - w]가 아직 '이 물건을 넣기 전' 값이라 한 번만 넣는다 for (int c = W; c >= w; c--) { best[c] = std::max(best[c], best[c - w] + v); } } std::cout << best[W] << "\n";}
Cmain.c · 19 줄
#include <stdio.h>int main(void) { int n, W; scanf("%d %d", &n, &W); /* best[c] = 무게 합이 c 이하일 때 얻는 가장 큰 가치(지금까지 본 물건만 써서) */ static int best[10000]; /* static이라 0으로 채워져 있다 */ for (int i = 0; i < n; i++) { int w, v; scanf("%d %d", &w, &v); /* 큰 무게부터 거꾸로: best[c - w]가 아직 '이 물건을 넣기 전' 값이라 한 번만 넣는다 */ for (int c = W; c >= w; c--) { if (best[c - w] + v > best[c]) best[c] = best[c - w] + v; } } printf("%d\n", best[W]); return 0;}
Craykonmain.cray · 19 줄
첫줄 = 콘솔.수들읽기();n = 첫줄[0];W = 첫줄[1];// best[c] = 무게 합이 c 이하일 때 얻는 가장 큰 가치(지금까지 본 물건만 써서)// 목록 하나에 10,000칸까지라 W + 1칸짜리 한 줄만 쓴다best = 새목록(W + 1, 0);반복 (i = 0; i < n; i++){ 물건 = 콘솔.수들읽기(); w = 물건[0]; v = 물건[1]; // 큰 무게부터 거꾸로: best[c - w]가 아직 '이 물건을 넣기 전' 값이라 한 번만 넣는다 반복 (c = W; c >= w; c--) { best[c] = 수학.큰수(best[c], best[c - w] + v); }}콘솔.쓰기(best[W]);