(转载)01背包问题(动态规划算法)

概述

有n个物品,第i个物品价值为v,重量为w,其中v和w均为非负数,背包的容量是W。现需要考虑如何选择装入背包的物品,使装入背包的物品总价值最大。

物品编号 1 2 3 4 5
价值v 4 5 10 11 13
重量w 3 4 7 8 9


解题思路

该问题以形式化描述如下
目标函数:
$$max\sum_{i=0}^{n}v_ix_i$$

约束条件:
$$\sum_{i=1}^{n}w_ix_i\leq{W},x_i\in{0,1}$$

0/1背包问题的最优解的结构(状态)

可以将背包问题的求解过程看作是进行一系列的决策过程,决定哪些物品应该放入背包,哪些物品不应该放入背包。放入背包物品有2种情况,如果一个问题的最优解包含物品n,那么其余的物品构成子问题,n-1在容量为W-wn时的最优解。如果这个最优解不包含物品n,其余问题肯定构成子问题,n-1在容量为W时的最优解。

定义最优解的值(状态转移方程)

c[i,w]:背包容量为w,i个物品导致的最优解的总价值。

1
2
3
             i=0:c[i,w] = 0
状态1 wi>w:c[i,w] = c[i-1,w]
状态2 i>0且wi<=w:max(c[c-1,w-wi]+vi,c[i-1,w]) 取范围内的最大值

程序代码

伪代码

n:数目
W:最大重量
时间复杂度:O(nW)

1
2
3
4
5
6
7
8
9
10
11
Knapsack-DP(n,W)
for w <- 0 to W
do c[0,w] <- 0
for i <- 1 to n
do c[i,0] <- 0
for w <- 1 to W
do if w[i] <= w //如果第 i 个背包不大于总承重,则最优解要么是包含第 i 个背包的最优解,要么是不包含第 i 个背包的最优解,取两者最大值
then if v[i] + c[i-1,w-w[i]] >= c[c-1,w]
then c[i,w] <- v[i] + c[i-1,w-w[i]]
else c[i,w] <- c[i-1,w]
else c[i,w] <- c[i-1,w] //i物品的重量大于w,最优解存在i-1物品

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include "T.h"
#include <iostream>
using namespace std;

# define N 11


/***
c[i][w]表示背包容量为w时,i个物品导致的最优解的总价值,大小为(n+1)*(w+1)
v[i]表示第i个物品的价值,大小为n
w[i]表示第i个物品的重量,大小为n
***/

void DP(int n, int W, int c[][18], int *v, int *wei)
{
memset(*c, 0, (W+1)*sizeof(int));
//遍历n个物品
for (int i = 1; i <= n; i++)
{
c[i][0] = 0;
//遍历每个重量
for (int w = 1; w <= W; w++)
{
//i物品的重量大于w,那最优解应该取i-1的物品
if (wei[i-1] > w)
{
c[i][w] = c[i-1][w];
}
else
{
//i物品的重量不大于w,最优解可能包含i物品,可能不包含i物品
int temp = c[i-1][w-wei[i-1]] + v[i-1]; //注意wei和v数组中的第i个应该为wei[i-1]和v[i-1]
if (c[i-1][w] > temp)
{
c[i][w] = c[i-1][w];
}
else
c[i][w] = temp;
}
}
}
}

void findPath(int c[][18], int *x, int *wei, int n, int W)
{
int w = W;
for (int i = n; i >= 2; i--)
{
//i物品和i-1物品价值相等
if (c[i][w] == c[i-1][w])
{
//标记0
x[i-1] = 0;
}
else
{
//标记1
x[i-1] = 1;
//重量减少
w = w - wei[i-1];
}
}
if (c[1][w] == 0){//第1个物品判断是否存放背包里
x[0] = 0;
}else{
x[0] = 1;
}
}

int main()
{
//数目
int n = 5;
//总重量
int W = 17;
//重量数组
int w[] = {3, 4, 7, 8, 9};
//物品价值数组
int v[] = {4, 5, 10, 11, 13};
int c[6][18] = {0};
DP(n, W, c, v, w);
cout<<c[5][17]<<endl;
int x[5];
findPath(c, x, w, n, W);
for (int i = 0; i < n; i++){
cout<<x[i]<<" ";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<queue>
#include<climits>
#include<cstring>
using namespace std;
const int c = 10; //背包的容量
const int w[] = {0,2,2,6,5,4};//物品的重量,其中0号位置不使用 。
const int v[] = {0,6,3,5,4,6};//物品对应的待加,0号位置置为空。
const int n = sizeof(w)/sizeof(w[0]) - 1 ; //n为物品的个数
int x[n+1];
void package0_1(int m[][11],const int w[],const int v[],const int n)//n代表物品的个数
{
//采用从底到顶的顺序来设置m[i][j]的值
//首先放w[n]
for(int j = 0; j <= c; j++){
if(j < w[n])
{
m[n][j] = 0; //j小于w[n],所对应的值设为0,否则就为可以放置
}else{
m[n][j] = v[n];
}
}
//对剩下的n-1个物品进行放置。
int i;
for(i = n-1; i >= 1; i--)
for(int j = 0; j <= c; j++)
if(j < w[i]) {
m[i][j] = m[i+1][j];//如果j < w[i]则,当前位置就不能放置,它等于上一个位置的值。
}else{
m[i][j] = m[i+1][j] > m[i+1][j-w[i]] + v[i]? m[i+1][j] : m[i+1][j-w[i]] + v[i]; //否则,就比较到底是放置之后的值大,还是不放置的值大,选择其中较大者。
}
}
void answer(int m[][11],const int n)
{
int j = c;
int i;
//遍历n-1个物品
for(i = 1; i <= n-1; i++)
{
if(m[i][j] == m[i+1][j]){
x[i] = 0;
}else{
x[i] = 1;
j = j - w[i];
}
}
//判断n物品,是否放入背包
x[n] = m[i][j] ? 1 : 0;
}
int main()
{
int m[6][11]={0};

package0_1(m,w,v,n);
for(int i = 0; i <= 5; i++)
{
for(int j = 0; j <= 10; j++)
printf("%2d ",m[i][j]);
cout << endl;
}
answer(m,n);
cout << "The best answer is:\n";
for(int i = 1; i <= 5; i++){
cout << x[i] << " ";
}
system("pause");
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package demo1;

public class Knapsack {

/** 背包重量 */
private int weight;

/** 背包物品价值 */
private int value;

/***
* 构造器
*/
public Knapsack(int weight, int value) {
this.value = value;
this.weight = weight;
}

public int getWeight() {
return weight;
}

public int getValue() {
return value;
}

public String toString() {
return "[weight: " + weight + " " + "value: " + value + "]";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package demo1;

import java.util.ArrayList;

/**
* 求解背包问题: 给定 n 个背包,其重量分别为 w1,w2,……,wn, 价值分别为 v1,v2,……,vn 要放入总承重为 totalWeight
* 的箱子中, 求可放入箱子的背包价值总和的最大值。
*
* NOTE: 使用动态规划法求解 背包问题 设 前 n 个背包,总承重为 j 的最优值为 v[n,j], 最优解背包组成为 b[n]; 求解最优值: 1.
* 若 j < wn, 则 : v[n,j] = v[n-1,j]; 2. 若 j >= wn, 则:v[n,j] = max{v[n-1,j], vn +
* v[n-1,j-wn]}。
*
* 求解最优背包组成: 1. 若 v[n,j] > v[n-1,j] 则 背包 n 被选择放入 b[n], 2. 接着求解前 n-1 个背包放入 j-wn
* 的总承重中, 于是应当判断 v[n-1, j-wn] VS v[n-2,j-wn], 决定 背包 n-1 是否被选择。 3. 依次逆推,直至总承重为零。
*
* 重点: 掌握使用动态规划法求解问题的分析方法和实现思想。 分析方法: 问题实例 P(n) 的最优解S(n) 蕴含 问题实例 P(n-1)
* 的最优解S(n-1); 在S(n-1)的基础上构造 S(n) 实现思想: 自底向上的迭代求解 和 基于记忆功能的自顶向下递归
*/
public class KnapsackProblem {

/** 指定背包 */
private Knapsack[] bags;

/** 总承重 */
private int totalWeight;

/** 给定背包数量 */
private int n;

/** 前 n 个背包,总承重为 totalWeight 的最优值矩阵 */
private int[][] bestValues;

/** 前 n 个背包,总承重为 totalWeight 的最优值 */
private int bestValue;

/** 前 n 个背包,总承重为 totalWeight 的最优解的物品组成 */
private ArrayList<Knapsack> bestSolution;

public KnapsackProblem(Knapsack[] bags, int totalWeight) {
this.bags = bags;
this.totalWeight = totalWeight;
this.n = bags.length;
if (bestValues == null) {
bestValues = new int[n + 1][totalWeight + 1];
}
}

/**
* 求解前 n 个背包、给定总承重为 totalWeight 下的背包问题
*
*/
public void solve() {

System.out.println("给定背包:");
for (Knapsack b : bags) {
System.out.println(b);
}
System.out.println("给定总承重: " + totalWeight);

// 求解最优值
for (int j = 0; j <= totalWeight; j++) {
for (int i = 0; i <= n; i++) {

if (i == 0 || j == 0) {
bestValues[i][j] = 0;
} else {
// 如果第 i 个背包重量大于总承重,则最优解存在于前 i-1 个背包中,
// 注意:第 i 个背包是 bags[i-1]
if (j < bags[i - 1].getWeight()) {
bestValues[i][j] = bestValues[i - 1][j];
} else {
// 如果第 i 个背包不大于总承重,则最优解要么是包含第 i 个背包的最优解,
// 要么是不包含第 i 个背包的最优解,取两者最大值,这里采用了分类讨论法
// 第 i 个背包的重量 iweight 和价值 ivalue
int iweight = bags[i - 1].getWeight();
int ivalue = bags[i - 1].getValue();
bestValues[i][j] = Math.max(bestValues[i - 1][j], ivalue + bestValues[i - 1][j - iweight]);
} // else
} // else
} // for
} // for

// 求解背包组成
if (bestSolution == null) {
bestSolution = new ArrayList<Knapsack>();
}
//总重量
int tempWeight = totalWeight;
for (int i = n; i >= 1; i--) {
if (bestValues[i][tempWeight] > bestValues[i - 1][tempWeight]) {
// bags[i-1] 表示第 i 个背包
bestSolution.add(bags[i - 1]);
tempWeight -= bags[i - 1].getWeight();
}
if (tempWeight == 0) {
break;
}
}
//前n个背包的最优解(价值)
bestValue = bestValues[n][totalWeight];
}

/**
* 获得前 n 个背包, 总承重为 totalWeight 的背包问题的最优解值 调用条件: 必须先调用 solve 方法
*
*/
public int getBestValue() {
return bestValue;
}

/**
* 获得前 n 个背包, 总承重为 totalWeight 的背包问题的最优解值矩阵 调用条件: 必须先调用 solve 方法
*
*/
public int[][] getBestValues() {

return bestValues;
}

/**
* 获得前 n 个背包, 总承重为 totalWeight 的背包问题的最优解值矩阵 调用条件: 必须先调用 solve 方法
*
*/
public ArrayList<Knapsack> getBestSolution() {
return bestSolution;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package demo1;

public class KnapsackTest {

public static void main(String[] args) {
Knapsack[] bags = new Knapsack[] {
new Knapsack(2,13), new Knapsack(1,10),
new Knapsack(3,24), new Knapsack(2,15),
new Knapsack(4,28), new Knapsack(5,33),
new Knapsack(3,20), new Knapsack(1, 8)
};

int totalWeight = 10;
KnapsackProblem kp = new KnapsackProblem(bags, totalWeight);

kp.solve();
System.out.println(" -------- 该背包问题实例的解: --------- ");
System.out.println("最优值:" + kp.getBestValue());
System.out.println("最优解【选取的背包】: ");
System.out.println(kp.getBestSolution());
System.out.println("最优决策矩阵表:");
int[][] bestValues = kp.getBestValues();
for (int i=0; i < bestValues.length; i++) {
for (int j=0; j < bestValues[i].length; j++) {
System.out.printf("%-5d", bestValues[i][j]);
}
System.out.println();
}
}
}

背包问题示例

一切都要从一则故事说起。
话说有一哥们去森林里玩发现了一堆宝石,他数了数,一共有n个。 但他身上能装宝石的就只有一个背包,背包的容量为C。这哥们把n个宝石排成一排并编上号:0,1,2,…,n-1。第i个宝石对应的体积和价值分别为V[i]W[i]。排好后这哥们开始思考: 背包总共也就只能装下体积为C的东西,那我要装下哪些宝石才能让我获得最大的利益呢?
OK,如果是你,你会怎么做?你斩钉截铁的说:动态规划啊!恭喜你,答对了。 那么让我们来看看,动态规划中最最最重要的两个概念: 状态和状态转移方程在这个问题中分别是什么。
我们要怎样去定义状态呢?这个状态总不能是凭空想象或是从天上掉下来的吧。 为了方便说明,让我们先实例化上面的问题。一般遇到n,你就果断地给n赋予一个很小的数,比如n=3。然后设背包容量C=10,三个宝石的体积为5,4,3,对应的价值为20,10,12。 对于这个例子,我想智商大于0的人都知道正解应该是把体积为5和3的宝石装到背包里, 此时对应的价值是20+12=32。接下来,我们把第三个宝石拿走, 同时背包容量减去第三个宝石的体积(因为它是装入背包的宝石之一), 于是问题的各参数变为:n=2,C=7,体积{5,4},价值{20,10}。好了, 现在这个问题的解是什么?我想智商等于0的也解得出了:把体积为5的宝石放入背包 (然后剩下体积2,装不下第二个宝石,只能眼睁睁看着它溜走),此时价值为20。 这样一来,我们发现,n=3时,放入背包的是0号和2号宝石;当n=2时, 我们放入的是0号宝石。这并不是一个偶然,没错, 这就是传说中的“全局最优解包含局部最优解”(n=2n=3情况的一个局部子问题)。 绕了那么大的圈子,你可能要问,这都哪跟哪啊?说好的状态呢?说好的状态转移方程呢? 别急,它们已经呼之欲出了。
我们再把上面的例子理一下。当n=2时,我们要求的是前2个宝石, 装到体积为7的背包里能达到的最大价值;当n=3时,我们要求的是前3个宝石, 装到体积为10的背包里能达到的最大价值。有没有发现它们其实是一个句式!OK, 让我们形式化地表示一下它们, 定义d(i,j)为前i个宝石装到剩余体积为j的背包里能达到的最大价值。 那么上面两句话即为:d(2, 7)d(3, 10)。这样看着真是爽多了, 而这两个看着很爽的符号就是我们要找的状态了。 即状态d(i,j)表示前i个宝石装到剩余体积为j的背包里能达到的最大价值。
上面那么多的文字,用一句话概括就是:根据子问题定义状态!你找到子问题, 状态也就浮出水面了。而我们最终要求解的最大价值即为d(n, C):前n个宝石(0,1,2…,n-1)装入剩余容量为C的背包中的最大价值。状态好不容易找到了, 状态转移方程呢?顾名思义,状态转移方程就是描述状态是怎么转移的方程。 那么回到例子,d(2, 7)d(3, 10)是怎么转移的?来,我们来说说2号宝石 (记住宝石编号是从0开始的)。从d(2, 7)d(3, 10)就隔了这个2号宝石。 它有两种情况,装或者不装入背包。如果装入,在面对前2个宝石时, 背包就只剩下体积7来装它们,而相应的要加上2号宝石的价值12,d(3, 10)=d(2, 10-3)+12=d(2, 7)+12;如果不装入,体积仍为10,价值自然不变了,d(3, 10)=d(2, 10)。记住,d(3, 10)表示的是前3个宝石装入到剩余体积为10 的背包里能达到的最大价值,既然是最大价值,就有d(3, 10)=max{d(2, 10), d(2, 7)+12}。好了,这条方程描述了状态d(i, j)的一些关系, 没错,它就是状态转移方程了。把它形式化一下:d(i, j)=max{ d(i-1, j), d(i-1,j-V[i-1]) + W[i-1]}。注意讨论前i个宝石装入背包的时候, 其实是在考查第i-1个宝石装不装入背包(因为宝石是从0开始编号的)。至此, 状态和状态转移方程都已经有了。接下来,直接上代码。

1
2
3
4
5
6
for(int i=0; i<=n; ++i){
for(int j=0; j<=C; ++j){
d[i][j] = i==0 ? 0 : d[i-1][j];
if(i>0 && j>=V[i-1]) d[i][j] = d[i][j] >d[i-1][j-V[i-1]]+W[i-1]?d[i][j] : d[i-1][j-V[i-1]]+W[i-1];
}
}

i=0时,d(i, j)为什么为0呢?因为前0个宝石装入背包就是没东西装入,所以最大价值为0。 if语句里,j>=V[i-1]说明只有当背包剩余体积j大于等于i-1号宝石的体积时, 我才考虑把它装进来的情况,不然d[i][j]就直接等于d[i-1][j]i>0不用说了吧, 前0个宝石装入背包的情况是边界,直接等于0,只有i>0才有必要讨论, 我是装呢还是不装呢。简单吧,核心算法就这么一丁点,接下来上完整代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**0-1 knapsack d(i, j)表示前i个物品装到剩余容量为j的背包中的最大重量**/
#include<cstdio>
using namespace std;
#define MAXN 1000
#define MAXC 100000

int V[MAXN], W[MAXN];
int d[MAXN][MAXC];

int main(){
freopen("data.in", "r", stdin);//重定向输入流
freopen("data.out", "w", stdout);//重定向输出流
int n, C;
while(scanf("%d %d", &n, &C) != EOF){
for(int i=0; i<n; ++i) scanf("%d %d", &V[i], &W[i]);

for(int i=0; i<=n; ++i){
for(int j=0; j<=C; ++j){
d[i][j] = i==0 ? 0 : d[i-1][j];
if(i>0 && j>=V[i-1]) d[i][j] = d[i][j] >d[i-1][j-V[i-1]]+W[i-1]?d[i][j] : d[i-1][j-V[i-1]]+W[i-1];
}
}
printf("%d\n", d[n][C]);//最终求解的最大价值
}
fclose(stdin);
fclose(stdout);
return 0;
}

好,至此我们解决了背包问题中最基本的0/1背包问题。等等,这时你可能要问, 我现在只知道背包能装入宝石的最大价值,但我还不知道要往背包里装入哪些宝石啊。嗯, 好问题!让我们先定义一个数组x,对于其中的元素为1时表示对应编号的宝石放入背包,为0则不放入。让我们回到上面的例子,对于体积为5,4,3,价值为20,10,12的3个宝石 ,如何求得其对应的数组x呢?(明显我们目测一下就知道x={1 0 1}, 但程序可目测不出来)OK,让我们还是从状态说起。如果我们把2号宝石放入了背包, 那么是不是也就意味着,前3个宝石放入背包的最大价值要比前2个宝石放入背包的价值大, 即:d(3, 10)>d(2, 10)。再用字母代替具体的数字,当d(i, j)>d(i-1, j)时,x(i-1)=1;OK, 上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**0-1 knapsack d(i, j)表示前i个物品装到剩余容量为j的背包中的最大重量**/
#include<cstdio>
using namespace std;
#define MAXN 1000
#define MAXC 100000

int V[MAXN], W[MAXN], x[MAXN];
int d[MAXN][MAXC];

int main(){
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
int n, C;
while(scanf("%d %d", &n, &C) != EOF){
for(int i=0; i<n; ++i) scanf("%d %d", &V[i], &W[i]);
for(int i=0; i<n; ++i) x[i] = 0; //初始化打印方案

for(int i=0; i<=n; ++i){
for(int j=0; j<=C; ++j){
d[i][j] = i==0 ? 0 : d[i-1][j];
if(i>0 && j>=V[i-1]) d[i][j] >?= d[i-1][j-V[i-1]]+W[i-1];
}
}
printf("%d\n", d[n][C]);

//输出打印方案
int j = C;
for(int i=n; i>0; --i){
if(d[i][j] > d[i-1][j]){
x[i-1] = 1;
j = j - V[i-1];
}
}
for(int i=0; i<n; ++i) printf("%d ", x[i]);
printf("\n");
}
fclose(stdin);
fclose(stdout);
return 0;
}

data.out输出结果变为:

1
2
3
4
5
6
19
1 1 0 1 0
40
1 0 1 0
15
1 1 0 0 1

至此,好像该解决的问题都解决了。当一个问题找到一个放心可靠的解决方案后, 我们往往就要考虑一下是不是有优化方案了。为了保持代码的简洁, 我们暂且把宝石装包方案的求解去掉。该算法的时间复杂度是O(nC), 即时间都花在两个for循环里了,这个应该是没办法再优化了。再看看空间复杂度, 数组d用来保存每个状态的值,空间复杂度为O(nC); 数组V和W用来保存每个宝石的体积和价值,空间复杂度为O(n)。程序总的空间复杂度为O(nC),这个是可以进一步优化的。首先,我们先把数组V和W去掉, 因为它们没有保存的必要,改为一边读入一边计算。
好了,接下来让我们继续压榨空间复杂度。保存状态值我们开了一个二维数组d, 在看过把一维数组V和W变为一个变量后,我们是不是要思考一下,有没有办法将这个二维数组也压榨一下呢?换言之, 这个二维数组中的每个状态值我们真的有必要都保存么? 让我们先来看一下以下的一张示意图(参照《算法竞赛入门经典》P169的图画的)

由上面那一小段优化过后的代码可知,状态转移方程为:d(i, j)=max{ d(i-1, j), d(i-1, j-V)+W},也就是在计算d(i, j)时我们用到d(i-1,j)d(i-1, j-V)的值。 如果我们只用一个一维数组d(0)~d(C)来保存状态值可以么?将i方向的维数去掉,我们可以将原来二维数组表示为一维数据:d(i-1, j-V)变为d(j-V)d(i-1, j)变为d(j)。当我们要计算d(i, j)时,只需要比较d(j)d(j-V)+W的大小,用较大的数更新d(j)即可。等等,如果我要计算d(i, j+1),而它恰好要用到d(i-1, j)的值, 那么问题就出来了,因为你刚刚才把它更新为d(i, j)了。那么,怎么办呢? 按照j递减的顺序即可避免这种问题。比如,你计算完d(i, j), 接下来要计算的是d(i,j-1),而它的状态转移方程为d(i, j-1)=max{ d(i-1, j-1), d(i-1, j-1-V)+W },它不会再用到d(i-1,j)的值!所以, 即使该位置的值被更新了也无所谓。好,上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**0-1 knapsack d(i, j)表示前i个物品装到剩余容量为j的背包中的最大重量**/
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;

int main(){
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
int n, C, V = 0, W = 0;
while(scanf("%d %d", &n, &C) != EOF){
int* d = (int*)malloc((C+1)*sizeof(int));
memset(d, 0, (C+1)*sizeof(int));

for(int i=0; i<=n; ++i){
if(i>0) scanf("%d %d", &V, &W);
for(int j=C; j>=0; --j){
if(j>=V && i>0) d[j] >?= d[j-V]+W;
}
}
printf("%d\n", d[C]);
free(d);
}
fclose(stdin);
fclose(stdout);
return 0;
}

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×