简单DP的小见解(入门背包)
n为个数,m为大小,v[]为单个物品体积,w[]为此物品的价值
01背包(每种物品只能选0件或者1件)
引例:假设小偷去珠宝店盗窃,他的背包容量为C=10,珠宝店里有3件珠宝可以盗窃,问可以装入背包的最大价值是多少。
想要求装入背包的最大价值,肯定要在尽量装满背包的情况下拿走价值尽可能高的物品,使得最后的总价值最大化
dp[n] [m]为第n件物品剩余m空间
第i件物品是否可以拿,存在两种情况:
(1)剩余空间不能装下此物品,那么问题转化为将前i-1件物品放入背包中所获得的最大价值.。
(2)剩余空间可以装下此物品,想要求i件物品最优解,对于此物品又面临拿与不拿两种情况:
a.拿走此物品,此时的价值就应该是(i-1)件物品时的最优解加上此物品的价值。相应的,因为拿走了这件物
品,空间也会对应减少,此时的状态即:dp[i-1] [j-space[i]]+val[i]
b.不拿此物品,此时的价值就应该与拿走前(i-1)件物品获得的最大价值相同,虽然没有拿走这个物品,但是空间也没有减少,这意味着剩下的空间还可以装别的东西。此时状态为:dp[i-1] [j]
得出判断条件
(二维数组)
if(j<space[i]) //剩余空间小于该物品体积
dp[i][j]=dp[i-1][j];
else
dp[i][j]=max(dp[i-1][j],dp[i-1][j-space[i]]+val[i]);
(简化为一维数组)
for (int i = 0; i < n; i++) {
for (int j = m; j >= v[i]; j--) {
dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
}
}
(洛谷P1060)
m背包大小 n物品个数
输入 v[]物品大小 p[]物品价值
输出 在背包内装下物品的 v[]*p[];
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 30000 + 5;
int v[maxn], p[maxn], w[maxn];
int dp[maxn];
int main()
{
int n, m;
cin >> m >> n;
for (int i = 0; i < n; i++) {
cin >> v[i] >> p[i];
w[i] = v[i] * p[i];
}
for (int i = 0; i < n; i++) {
for (int j = m; j >= v[i]; j--) {
dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
}
}
cout << dp[m] << endl;
return 0;
}
完全背包(每种物品可以选择0件至多件,没有限制)
利用以为数组,完全背包与01背包的差异就只有循环方向了
题(洛谷p1616)
for (int i = 0; i < n; i++) {
for (int j = v[i]; j <= m; j++) {
dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
}
}
多重背包(第i种物品最多有n[i]件可用)
多重背包求解即转化为01背包求解:
**思想是:**把第i种物品换成n[i]件01背包中的物品。考虑二进制的思想,考虑把第i种物品换成若干件物品,使得原问题中第i种物品可取的每种策略——取0…n[i]件——均能等价于取若干件代换以后的物品。另外,取超过n[i]件的策略必不能出现。
**方法是:**将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为1,2,4,…,2(k-1),n[i]-2k+1,且k是满足n[i]-2^k+1>0的最大整数。例如,如果n[i]为13,就将这种物品分成系数分别为1,2,4,6的四件物品。分成的这几件物品的系数和为n[i],表明不可能取多于n[i]件的第i种物品。另外这种方法也能保证对于0…n[i]间的每一个整数,均可以用若干个系数的和表示。
hdu 2844
题意:有n种货币和手表价格为m,单位货币的价值有val[i],单位货币的数量有vol[i],。求解带哪些 货币val*货币 数量vol 可将手表买回且身上的钱总价值最大题解
输入:n,m;(v1,v2,vn,number1,number2,number n)
输出:答案占一行
这道题和poj 1742一样,只不过poj上面使用STL里面的max函数会超时,将max函数改为if判断语句即可
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <cstring>
#define MAX 1000000
using namespace std;
int dp[MAX];//存储最后背包最大能存多少
int value[MAX], weight[MAX], number[MAX];//分别存的是物品的价值,每一个的重量以及数量
int bag;
void ZeroOnePack(int weight, int value)//01背包
{
int i;
for (i = bag; i >= weight; i--)
{
dp[i] = max(dp[i], dp[i - weight] + value);
}
}
void CompletePack(int weight, int value)//完全背包
{
int i;
for (i = weight; i <= bag; i++)
{
dp[i] = max(dp[i], dp[i - weight] + value);
}
}
void MultiplePack(int weight, int value, int number)//多重背包
{
if (bag <= number * weight)//如果总容量比这个物品的容量要小,那么这个物品可以直到取完,相当于完全背包
{
CompletePack(weight, value);
return;
}
else//否则就将多重背包转化为01背包
{
int k = 1;
while (k <= number)
{
ZeroOnePack(k*weight, k*value);
number = number - k;
k = 2 * k;//这里采用二进制思想
}
ZeroOnePack(number*weight, number*value);
}
}
int main()
{
int n;
while (~scanf("%d%d", &n, &bag))
{
if (n == 0 && bag == 0)
break;
int i, sum = 0;
for (int i = 0; i < n; i++)
scanf("%d", &value[i]);//输入价值,在没有物体质量的情况下,可以理解为质量和价值相等
for (int i = 0; i < n; i++)
scanf("%d", &number[i]);//输入数量
memset(dp, 0, sizeof(dp));
for (i = 0; i < n; i++)
{
MultiplePack(value[i], value[i], number[i]);//调用多重背包,注意穿参的时候分别是重量,价值和数量
}
int ans = 0;
for (int i = 1; i <= bag; i++) {
if (i == dp[i])
ans++;
}
cout << ans << endl;
}
return 0;
}
来源:CSDN
作者:♡-流浪孩
链接:https://blog.csdn.net/qq_43321732/article/details/103690099