01背包问题
有n个重量和价值分别为Wi,Vi的物品。从这些物品中挑选出总重量不超过W的物品,求所有挑选方案中价值总和的最大值。
1<=N<=100
1<=Wi,Vi<=100
1<=W<=10000
第一行输入n的值
接下来n行输入Wi,Vi
最后一行输入W
For example:
Input:
4
2 3
1 2
3 4
2 2
5
Output:
7
在解决01背包问题,我们将N件物品拆分成N步,每次从N件物品中拿出一件,并于0~W重量中的最优价值进行比较(前提是那件物品能够放的下)。
所以我们需要一个辅助的dp[j]数组,j表示剩余空间的量。
初始dp数组值为0(因为没有物品放入价值当然为0)
求n件物品在重量在W内的最大价值,我们就必须求出:
1件物品在重量为1的最优价值;1件物品在重量为2的最优价值...1件物品在重量为W的最优价值。
1件物品在重量为1的最优价值;1件物品在重量为2的最优价值...1件物品在重量为W的最优价值。
当j<w[i],也就是物品放不下的时候保持原来的价值
当j>=w[i]时,我们就要比较原数值与“腾出”来w[i]单位的价值+该物品的价值。选择较大的值进行覆盖。(因为可能会用到j之前的数值,所以j必须从高位向低位移动)
公式:dp[j] = max(dp[j], dp[j - w[i]] + v[i]) (j >= w[i])
最后dp[W]就是解得值。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
void solve(int dp[], int n, int weight, int w[], int v[]);
int main()
{
int n, w[101], v[101], dp[101], i, j, W;
while (~scanf("%d", &n))
{
for (i = 0; i<n; i++)
scanf("%d %d", &w[i], &v[i]);
scanf("%d", &W);
memset(dp, 0, sizeof(dp));
solve(dp, n, W, w, v);
printf("%d\n", dp[W]);
}
return 0;
}
void solve(int dp[], int n, int W, int w[], int v[])
{
for (int i = n - 1; i >= 0; i--)
for (int j = W; j >= w[i]; j--) //逆序填表
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
完全背包
有n种重量和价值分别为Wi,Vi的物品。从这些物品中挑选总重量不超过W的物品,求出挑选物品价值总和的最大值。在这里,每种物品可以挑选任意多件。
For example:
Input:
3
3 4
4 5
2 3
7
Output:
10
完全背包与01背包解题思路一样,只不过在是否重复放第i件物品时,与j-w[i]的机制一样,所以放与不放取决于dp[j-w[i]]的值,所以填表顺序必须正序填充。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
void solve(int dp[101], int w[], int v[], int n, int W);
int main()
{
int w[101] = { 0 }, v[101] = { 0 }, dp[101] = { 0 }, n, i, W;
while (~scanf("%d", &n))
{
for (i = 0; i<n; i++)
scanf("%d %d", &w[i], &v[i]);
scanf("%d", &W);
solve(dp, w, v, n, W);
printf("%d\n", dp[W]);
memset(dp, 0, sizeof(dp));
memset(w, 0, sizeof(w));
memset(v, 0, sizeof(v));
}
return 0;
}
void solve(int dp[101], int w[], int v[], int n, int W)
{
for (int i = n - 1; i >= 0; i--)
for (int j = 1; j <= W; j++) //正序填表
if (j >= w[i])
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}