背包问题应该都挺熟了,但还是放一下题目
无论是一维还是二维的解法,思路都比较一致,就是用一个二维的
d
p
dp
dp矩阵,
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 的定义为前
i
i
i 个元素的最优组合在容量为
j
j
j 的背包的最大价值。
这个定义非常的巧妙并且很难直接想到,因此,很多dp题目会以背包作为变种,因此,背包是dp中非常经典的例子。
首先看一维的做法,如下所示,比较简单,注意细节:在内层循环中,如果 j < w j < w j<w了,可以直接返回。
#include <bits/stdc++.h>
//#include<ext/pb_ds/assoc_container.hpp>
//#include<ext/pb_ds/trie_policy.hpp>
//using namespace __gnu_pbds;
typedef long long ll;
using namespace std;
typedef pair<int, int> P;
int INF = 1 << 30;
int main()
{
int N, W;
scanf("%d %d", &N, &W);
int dp[W+1];
memset(dp, 0, sizeof(dp));
for(int i=0;i<N;i++)
{
int v, w;
scanf("%d %d", &w, &v);
for(int j=W;j>=w;j--)
{
dp[j] = max(dp[j], dp[j-w]+v);
}
}
printf("%d\n", dp[W]);
}
接下来看二维的做法,首先讲一下,为什么需要二维的做法,因为一维无法满足部分题目的条件,比如说,如果需要我们找到放进背包具体是哪几个物品,一维的是无法保存历史信息的,在这个例子下我们就需要保存二维矩阵,再进行回溯。
接下来看二维的代码,注意到就算 j < w j < w j<w 了,我们也不能跳出循环(这个会多消耗一些时间,但影响不大)而是要把上面的结果复制下来,算是和一维最重要的区别了。
#include <bits/stdc++.h>
using namespace std;
//#include<ext/pb_ds/assoc_container.hpp>
//#include<ext/pb_ds/trie_policy.hpp>
//using namespace __gnu_pbds;
typedef long long ll;
typedef vector<int> V;
// int MOD = 1e9 + 7;
int main()
{
int N, W;
scanf("%d %d", &N, &W);
vector<V> dp(N+1, V(W+1, 0));
for(int i=1;i<=N;i++)
{
int v, w;
scanf("%d %d", &w, &v);
for(int j=W;j>=0;j--)
{
if(j-w < 0)dp[i][j] = dp[i-1][j];
else dp[i][j] = max(dp[i-1][j], dp[i-1][j-w]+v);
}
}
printf("%d\n", dp[N][W]);
}