总结(完全背包)

完全背包问题

问题描述:
有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是w[i],价值是val[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

解题思路:完全背包问题与0/1背包问题不同之处在于其每个物品是无限的,从每种物品的角度考虑,与它相关的策略就变成了取0件、1件、2件…。我们可以根据0/1背包的思路,对状态转移方程进行改进,令f[i][v]表示前
i 种物品恰放入一个容量为 v 的背包的最大权值。状态转移方程就变成了:

                              f[ i ][ v ] = max{ f[ i-1 ][ v-k*w[i] ] + k*val[ i ]  | 0 <= k*w[i] <=  v}

原文链接:https://blog.csdn.net/weixin_41162823/article/details/87878853

解题思路比较难理解,0/1背包是有限物品个数的选取和放入背包,对于完全背包的完全二字,我目前的理解是筛选范围的区别,01背包的物品数量有限制,对于01背包而言,给出的物品论个算,而对于完全背包而言,给出的物品论种算,没有数目的限制,目的只是为了装出最优解。01背包有种非0及1的感觉,而完全背包是有大局观的感觉,可以舍去一种情况求出更优的情况。

 仅仅在于更新当前dp[i][j]时上一次状态的来源,一个是从上一行dp[i-1]更新dp[i][j],另一个是从当前行dp[i]更新dp[i][j]**01背包:dp[i][j] = dp[i-1][j] or dp[i-1][j-nums[i]] 完全背包:dp[i][j] =dp[i-1][j] or dp[i][j-nums[i]]**
 
 
原文链接:https://blog.csdn.net/sunlanchang/article/details/109010244

公式不同的地方在于,01背包是源于上一个单位的个体进行更新,完全背包是源于上一个行进行更新
从csdn上搜索的两者在于公式上的区别,后期还可以进行优化

3.3 时间优化:根据上述f[ i ][ v ]的定义,其为前 i 种物品恰好放入容量为 v 的背包的最大权值。根据上述状态转移方程可知,我们假设的是子结果f[ i-1 ][ v-k*w[i] ]中并没有选入第 i
种物品,所以我们需要逆序遍历(像0/1背包一样)来确保该前提;但是我们现在考虑“加选一件第 i 种物品”这种策略时,正需要一个可能已经选入第
i 种物品的子结果f[ i ][ v-w[i] ],于是当我们顺序遍历时,就刚好达到该要求。这种做法,使我们省去了一层循环,即第 i
种物品放入的件数k,从而时间复杂度优化为O(n^2)。

3.4 空间优化:正如0/1背包的空间优化,上述状态转移方程已经优化为:

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

将这个方程用一维数组实现,便得到了如下伪代码:

for i = 1..N
    for v = 0..V
    f[v] = max{f[v],f[v-w[i]] + val[ i ] };

如果忘记了伪代码的含义,点击这里

3.5 小剪枝:完全背包问题有一个很简单有效的优化,是这样的:若两件物品i、j满足w[i] <= w[j]且val[i] >= val[j],则将物品j去掉,不用考虑。这个优化的正确性显然:任何情况下都可将价值小费用高的j换成物美价廉的i,得到至少不会更差的方案。对于随机生成的数据,这个方法往往会大大减少物品的件数,从而加快速度。然而这个并不能改善最坏情况的复杂度,因为有可能特别设计的数据可以一件物品也去不掉。
——————————
3.6 转化为0/1背包问题:
——————————
既然01背包问题是最基本的背包问题,那么我们可以考虑把完全背包问题转化为01背包问题来解。最简单的想法是,考虑到第i种物品最多选V/w[i]件,于是可以把第i种物品转化为V/w[i]件费用及价值均不变的物品,然后求解这个01背包问题。这样完全没有改进基本思路的时间复杂度,但这毕竟给了我们将完全背包问题转化为01背包问题的思路:将一种物品拆成多件物品。
——————————
更高效的转化方法是:把第i种物品拆成费用为w[i]*2k,价值为val[i]*2k 价值为val[i]*2k 的若干件物品,其中k满足w[i]*2k<V
这是二进制的思想,因为不管最优策略选几件第i种物品,总可以表示成若干个2k件物品的和。这样把每种物品拆成O(log(V/w[i])+1)件物品,是一个很大的改进。
——————————
版权声明:本文为CSDN博主「迷亭1213」的原创文章,遵循CC 4.0
BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_41162823/article/details/87878853

01背包和完全背包可以相互转化,由上文的3.6可以知道,背包问题中的最基本问题是01背包(但是我感觉01背包中的最基础问题是贪心,而事实上确实很多的01背包问题是用贪心来解决的)
用贪心解决的背包问题

  1. 例题一
  2. 较为系统的一道简单题
  3. 例题三

完全背包问题是在01背包的基础上对物品(数据)进行扩张(给出更多的数据和选择),其题目立意更加符合现实和大规模生产的状况,所以考虑的东西更多。

–————————————————
凑硬币应该是最典型的完全背包了吧?

Suppose there are 5 types of coins: 50-cent, 25-cent, 10-cent, 5-cent,and 1-cent. We want to make changes with these coins for a given amount of money. For example, if we have 11 cents, then we can make changes with one 10-cent coin and one 1-cent coin, two 5-cent coins and one 1-cent coin, one 5-cent coin and six 1-cent coins, or eleven1-cent coins. So there are four ways of making changes for 11 cents with the above coins. Note that we count that there is one way of making change for zero cent. Write a program to find the total number of different ways of making changes for any amount of money in cents. Your program should be able to handle up to 7489 cents.

假设有5种硬币:50美分、25美分、10美分、5美分和1美分。我们想用这些硬币换一定数量的钱。例如,如果我们有11美分,那么我们可以用一个10美分的硬币和一个1美分的硬币,两个5美分的硬币和一个1美分的硬币,一个5美分的硬币和六个1美分的硬币,或者eleven1美分的硬币进行更改。因此,有四种方法来改变11美分与上述硬币。请注意,我们认为有一种方法可以改变零美分。写一个程序,找出所有不同的方法,使任何金额的钱在美分的变化总数。你的程序应该可以处理高达7489美分。

Input

The input file contains any number of lines, each one consisting of a number for the amount of money
in cents.

Output

For each input line, output a line containing the number of different ways of making changes with the
above 5 types of coins.

Sample Input

11
26

Sample Output

4
13

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int v[6];
int dp[100001];
int main()
{
    int n;
    v[0]=1,v[1]=5,v[2]=10,v[3]=25,v[4]=50;//直接用设定的数组数据来保存给出的少量数据
    while(~scanf("%d",&n))//读到文档结束
    {
        memset(dp,0,sizeof dp);//对dp数组赋初值0
        dp[0]=1;
        for(int i=0;i<5;i++)//有点穷举暴力的感觉
            for(int j=v[i];j<=n;j++)
                dp[j]=dp[j-v[i]]+dp[j];
        cout <<dp[n]<<endl;
     }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值