简单DP的小见解(入门背包)

简单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;
}
发布了40 篇原创文章 · 获赞 6 · 访问量 1422

猜你喜欢

转载自blog.csdn.net/qq_43321732/article/details/103690099