我知道这是一个老问题。但是我不得不花一些时间来寻找这个,我只是在这里记录这些方法以供任何人将来参考。
方法 1
使用 N 行的直接 2D 方法是:
int dp[MAXN][MAXW];
int solve()
{
memset(dp[0], 0, sizeof(dp[0]));
for(int i = 1; i <= N; i++) {
for(int j = 0; j <= W; j++) {
dp[i][j] = (w[i] > j) ? dp[i-1][j] : max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]);
}
}
return dp[N][W];
}
这使用 O(NW) 空间。
方法 2
您可能会注意到,在计算特定行的矩阵条目时,我们只查看前一行而不是之前的行。这可以被利用来仅维护 2 行并保持将它们的角色交换为当前行和上一行。
int dp[2][MAXW];
int solve()
{
memset(dp[0], 0, sizeof(dp[0]));
for(int i = 1; i <= N; i++) {
int *cur = dp[i&1], *prev = dp[!(i&1)];
for(int j = 0; j <= W; j++) {
cur[j] = (w[i] > j) ? prev[j] : max(prev[j], prev[j-w[i]] + v[i]);
}
}
return dp[N&1][W];
}
这需要 O(2W) = O(W) 空间。cur
是第 i 行,prev
是第 (i-1) 行。
方法 3
如果再看一遍,您会发现,当我们在一行中写入一个条目时,我们只查看了上一行中左侧的项目。我们可以使用它来使用单行并从右到左处理它,这样当我们为一个条目计算新值时,它左边的条目具有它们的旧值。这是一维表方法。
int dp[MAXW];
int solve()
{
memset(dp, 0, sizeof(dp));
for(int i =1; i <= N; i++) {
for(int j = W; j >= 0; j--) {
dp[j] = (w[i] > j) ? dp[j]: max(dp[j], dp[j-w[i]] + v[i]);
}
}
return dp[W];
}
这也使用 O(W) 空间,但只使用单行。必须反转内循环的主要原因是因为当我们使用 时dp[j-w[i]]
,我们需要外循环上一次迭代的值。为此,j
必须以从大到小的方式处理这些值。
测试用例(来自http://www.spoj.com/problems/PARTY/)
N = 10, W = 50
w[] = {0, 12, 15, 16, 16, 10, 21, 18, 12, 17, 18} // 1 based indexing
v[] = {0, 3, 8, 9, 6, 2, 9, 4, 4, 8, 9}
答案 = 26