01背包+完全背包+多重背包

问题:

一、01背包:给你n种不同的物品,每个物品有自己的重量weight[i],和价值value[i],如果每个物品只有一个,给你容量为V的背包,怎样才能取得最大价值?

-------------------------------------------------------------------分割线-----------------------------------------------------------------------------------

二、完全背包:给你n种不同的物品,每个物品有自己的重量weight[i],和价值value[i],如果每个物品有无限多,给你容量为V的背包,怎样才能取得最大价值?

-------------------------------------------------------------------分割线-----------------------------------------------------------------------------------

三、多重背包:给你n种不同的物品,每个物品有自己的重量weight[i],和价值value[i],如果每个物品只有c[i]个,给你容量为V的背包,怎样才能取得最大价值?

-------------------------------------------------------------------分割线-----------------------------------------------------------------------------------

 先来看01背包问题:

https://blog.csdn.net/na_beginning/article/details/62884939#commentsedit这个表格做的不错,不过代码嘛。。。。。

https://blog.csdn.net/qq_34374664/article/details/52230368  这个分析不错。不过建议只看01背包的问题,它解析的还是不错的。下面两个题目有点不一样还是怎么回事,过程解释是对的,但代码跟预期不同。

避免麻烦还是贴一下:

每种物品仅有一件,可以选择放或不放。

其实就是把背包的上限从改成1-m,求i为上限的最大价值

用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便是:

f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}

把这个过程理解下:在前i件物品放进容量v的背包时,

它有两种情况:

第一种是第i件不放进去,这时所得价值为:f[i-1][v]

第二种是第i件放进去,这时所得价值为:f[i-1][v-c[i]]+w[i]

(第二种是什么意思?就是如果第i件放进去,那么在容量v-c[i]里就要放进前i-1件物品)

最后比较第一种与第二种所得价值的大小,哪种相对大,f[i][v]的值就是哪种。

(这是基础,要理解!)

这里是用二位数组存储的,可以把空间优化,用一位数组存储。

用f[0..v]表示,f[v]表示把前i件物品放入容量为v的背包里得到的价值。把i从1~n(n件)循环后,最后f[v]表示所求最大值。

*这里f[v]就相当于二位数组的f[i][v]。那么,如何得到f[i-1][v]和f[i-1][v-c[i]]+w[i]?(重点!思考)
首先要知道,我们是通过i从1到n的循环来依次表示前i件物品存入的状态。即:for i=1..N
现在思考如何能在是f[v]表示当前状态是容量为v的背包所得价值,而又使f[v]和f[v-c[i]]+w[i]标签前一状态的价值?

                                                 逆序!

这就是关键!

核心代码是:

for (int i = 0; i < n; i++)
            for (int j = v; j >= weight[i]; j--)
                    record[j] = max(record[j],record[j-weight[i]]+value[i]);

record[j]用来记录当容量为j时的可行取法的最大价值。(记忆化搜索)

Bone Collector

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 81213    Accepted Submission(s): 33587

Problem Description

Many years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like to collect varies of bones , such as dog’s , cow’s , also he went to the grave …
The bone collector had a big bag with a volume of V ,and along his trip of collecting there are a lot of bones , obviously , different bone has different value and different volume, now given the each bone’s value along his trip , can you calculate out the maximum of the total value the bone collector can get ?

Input

The first line contain a integer T , the number of cases.
Followed by T cases , each case three lines , the first line contain two integer N , V, (N <= 1000 , V <= 1000 )representing the number of bones and the volume of his bag. And the second line contain N integers representing the value of each bone. The third line contain N integers representing the volume of each bone.

Output

One integer per line representing the maximum of the total value (this number will be less than 231).

Sample Input

1

5 10

1 2 3 4 5

5 4 3 2 1

Sample Output

14

【解析】

看一下HDU的这题,背景就不了解了,反正题意就是理解成01背包,东西变成了骨头而已。

一个T,表示案例数。

下面一行N,V。N表示东西个数,V表示背包容量。

再下面一行东西Ni的价值,在下面一行Ni所占的容量。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100;
int main()
{
	int t, weight[maxn], value[maxn], record[maxn];
	scanf("%d", &t);
	while (t--)
	{
		memset(record, 0, sizeof(record));
		int n, v;
		scanf("%d%d", &n, &v);
		for (int i = 0; i < n; i++)
			scanf("%d", &value[i]);
		for (int i = 0; i < n; i++)
			scanf("%d", &weight[i]);
		for (int i = 0; i < n; i++)//正序从第一个物品遍历到第n个物品
			for (int j = v; j >= weight[i]; j--)//逆序 每次从有V这么大的空间遍历到当前第i个物品的大小
				if (record[j - weight[i]] + value[i] > record[j])//如果拿第i个物品比不拿第i个物品价值大
					record[j] = record[j - weight[i]] + value[i];//那就拿第i个物品
		printf("%d\n", record[v]);
	}
	return 0;
}

完全背包问题:

跟01背包差不多一样,变的是我们拿几个:每种物品可以取0个、取1个、取2个……一直到背包放不下,即record[0],此时record[v]就是我们的答案。

所以状态转移方程也变了:

record[j] = max(record[j],record[j-k*weight[i]]+k*value[i])其中0<=k*weight[i]<=V

所以核心代码转变一下,从原来的逆序,变为正序:

for (int i = 0; i < n; i++)//上面对物品i
            for (int j = weight[i]; j <= v; j++)//拿一个的时候
                record[j] = max(record[j], record[j - weight[i]] + value[i]);

/*完全背包*/
/*测试用例
次数:t
种数、总空间:
N行每行分别表示Ni的价值、Ni所占空间
3
2 100
1 1
30 50

2 100
1 1
50 30

2 5
10 3
20 4
*/
#include <bits/stdc++.h>
using namespace std;
const int maxn = 110;
int main()
{
	int t, weight[maxn], value[maxn], record[maxn];
	scanf("%d", &t);
	while (t--)
	{
		memset(record, 0, sizeof(record));
		int n, v;
		scanf("%d%d", &n, &v);
		for (int i = 0; i < n; i++)
			scanf("%d%d", &value[i], &weight[i]);
		for (int i = 0; i < n; i++)
			for (int j = weight[i]; j <= v; j++)//这里可以调试一下,自行体会作用,很精妙,比用二维数组精妙的多
				record[j] = max(record[j], record[j - weight[i]] + value[i]);
		printf("%d\n", record[v]);
	}
	return 0;
}

最后对

多重背包的问题:

状态转移方程跟完全背包一模一样,但是条件要变一下,0<=k<=c[i]

for(i=0;i<n;i++)
      for(j=V;j>=0;j--)
           for(k=0;k<=c[i];k++) 
           {
                if(j-k*weight[i]<0)   break;
                record[j]=max(record[j],record[j-k*weight[i]]+k*value[i]);
          }

/*
测试用例:
1

2 5
1 1 2
10 3 1
*/

#include <bits/stdc++.h>
using namespace std;
const int maxn = 110;
int main()
{
	int t, weight[maxn], value[maxn], record[maxn], c[maxn];
	scanf("%d", &t);
	while (t--)
	{
		memset(record, 0, sizeof(record));
		int n, v;
		scanf("%d%d", &n, &v);
		for (int i = 0; i < n; i++)
			scanf("%d%d%d", &value[i], &weight[i], &c[i]);
		for (int i = 0; i < n; i++)
			for (int j = v; j >= 0; j--)
				for (int k = 0; k <= c[i]; k++)
				{
					if (j - k * weight[i] < 0)break;
					record[j] = max(record[j], record[j - k * weight[i]] + k * value[i]);
				}
		printf("%d\n", record[v]);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/waterboy_cj/article/details/81152431