网页资讯视频图片知道文库贴吧地图采购
进入贴吧全吧搜索

 
 
 
日一二三四五六
       
       
       
       
       
       

签到排名:今日本吧第个签到,

本吧因你更精彩,明天继续来努力!

本吧签到人数:0

一键签到
成为超级会员,使用一键签到
一键签到
本月漏签0次!
0
成为超级会员,赠送8张补签卡
如何使用?
点击日历上漏签日期,即可进行补签。
连续签到:天  累计签到:天
0
超级会员单次开通12个月以上,赠送连续签到卡3张
使用连续签到卡
04月07日漏签0天
算法吧 关注:31,036贴子:60,225
  • 看贴

  • 图片

  • 吧主推荐

  • 视频

  • 游戏

  • 12回复贴,共1页
<<返回算法吧
>0< 加载中...

求助通过有计数限制的完全背包实现多重背包,不知道可不可行。

  • 只看楼主
  • 收藏

  • 回复
  • 远河梦魂
  • 蔡基
    3
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
代码如图。题目是洛谷P1776。虽然不超时的用例都过了,但我仍然不能确定我这个混合了贪心思路的动态规划最终得到的是正解还是近似解。
一来,假如没有开头的价值密度排序,我这个算法就几乎不可能通过任何n>5的用例,轻而易举就能构造出一大堆反例,这让我严重怀疑动态规划在这里有没有起到作用。如果没有起到作用那这就是纯贪心,而纯贪心搞多重背包显然只可能得到近似解(甚至如果你打定主意用纯贪心近似的话,都可以直接O(n)复杂度,连我这O(nW)都省了)。
二来,我自己都不太理解我写的这个状态转移方程的实际意义,第一段套的是完全背包的模板,第二段单纯是为了防止dp[lbk]i[rbk][lbk]j[rbk]随着j增大可能减小的情况。你要说我完全一点主动设计都没有倒也不至于,但按照我原来的设想,根本不需要排序。
具体到某个用例上,我能理解为什么不排序过不去,否则也不会想到排序的方法;但上升到状态转移方程层面上,就又迷惑了。
ps: 超时不是复杂度问题,这题计算机专业的学长用C++写个常规的二进制分解01背包,O(nWlogn)复杂度,也能过,而这个O(nW)过不了单纯是因为python,但我只会写python。



  • 瓦妹
  • 达人
    10
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
能否说说你没有价值排序情况下的反例。我感觉这看起来没有价值排序情况下也是普通的完全背包解法
另外有一个bug。你的count table不是每次都重置的
如果排序后第一个物品w1=3,v=3。数量无限。第二个物品w2=5,v=4。count=1。那么最后5个重量应该给第二个物品。
因为数量无限,第一个if一定满足。因为是第一个物品,所以第二个if一定满足。于是不重置table。然后当考虑第二个物品时,count table存储的是第一个物品的信息。table[3]一开始就=1了,并且不会被w2=5修改。当你考虑table[8]时就会发现只放了一个物品1的背包不能放物品2了


2026-04-07 19:49:52
广告
不感兴趣
开通SVIP免广告
  • 远河梦魂
  • 蔡基
    3
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
@瓦妹
感谢大佬回答!
不排序就过不了的反例:
n=2,W=5
物品0:v=1,w=3,c=10(随便一个大于等于1的数)
物品1:ⅴ=3,w=1,c=2
第一轮dp = [0 0 0 1 1 1]
第二轮dp = [0 3 6 6 6 6]
但实际上第二轮dp的最优解应该是 [0 3 6 6 6 7]。
在i = 1, j = 5时,先查看count_table[5 - 1],发现数量已经是2,不能再加1,进入else;在else中它选取了max(dp[5 - 1], dp[5]),6 > 1(i=0那一层的),于是dp[5] = 6。而最优解是7。
排序之后就不一样了,3/1 > 1/3,物品1前置,
第一层dp = [0 3 6 6 6 6]
第二层dp = [0 3 6 6 6 7]
由于此时物品0的价值密度高于物品1,物品1在前期很难被用到,到i = 1, j = 5时,count_table[5 - 1]还是0,可以进入if,然后发现dp[5 - 1] + 1 = 7 > dp[5] = 6。
然后感谢大佬在线debug,我刚才试过了,在W=8时,用我的算法运行你这个用例,结果是6,而最优解显然是7,二进制拆分成01背包也能得到7。洛谷里那些用例能通过,反而是因为给的规模太大,W远大于w[i],c[i]也很大,让价值密度高的结果有充足的机会覆盖价值密度低的结果,没有暴露这个问题。如此一来,我更怀疑我这个算法得到的是近似解了(


  • 瓦妹
  • 达人
    10
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
我看出不排序的为什么有问题了。我这里先分析一下为什么你的方法要排序。对你解题没什么帮助,就是个分析为什么要排序。明天回来再看看能不能看出是近似解还是最优解。ok写着写着想出来了
先分析2d做法再回到你的做法
首先用最原始的dp[n+1][m+1]。dp[i][j]代表用前i个物品达到重量j时的最优解。此时状态转移方程是d[i][j]=max(d[i-1][j-wk]+vk)。i物品的重量,v为i物品的价值。k则是你要放几个该物品。也就是说,我们是从d[i-1]里面遍历来找到符合条件的。而d[i-1]是对前i-1个物品的总结,包含了这些组合的信息的总结。
而在你的的做法中,dp会被第i个物品单独的信息覆盖,而损失了对前i-1个物品的总结。
在你的例子中,第二轮时dp为[0 3 6 6 6 6]。dp[4]被i物品覆盖为6,dp[4]来自dp3来自dp2来自dp1。而dp[1]直接覆盖了第一轮的信息。因此损失了前i-1个物品的信息。而dp[5]更新时使用dp[4]。于是就出问题了
我有一个简易的想法可以替代排序。不过这个优化其实没卵用。毕竟运行时是由O(nw)主导的。看看就好
原解法中,我们更新dp[i]时只关心dp[i-1],不关心dp[0:i-2]。最后得出答案时只关心dp[n+1][w+1]。也就是说,你创建一个tempDP来处理dp[i],然后更新回你解法中的dp也是可以的。这样额外空间上还是O(W)
回到正题。不保证全对,欢迎挑错。从上面的分析可以看出,如果你的dp状态转移不会覆盖之前的信息,达到保留全部前i-1个物品信息的总结的作用,那就是最优解。
你的排序能保证前一轮保留下来的dp前面一定无法被替换的同时,前面还能放进物品刚好达到这个重量。我的意思是说,比如dp[k],当前物品有v和w。从k里拿出任意个w并且放入当前物品的同时,如果更优。那么前i-1个物品一定不能正好放满k重量。因为你排序了,单位重量的价值更多。
因此,只有放不满的dp[k]会可能被当前物品更新。而dp[k]更新基于dp[k-wb],b为你要放入几个物品。dp[k]基于dp[k-w]基于dp[k-2w],以此类推。最基础的那个一定是基于前i-1个物品的dp,它保留了全部信息。因此等价于原解法。
最有子结构你的算法是天生满足的。我不确定我这个解释是否算满足贪心选择性质


  • 瓦妹
  • 达人
    10
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
我发现我有写错的地方。原解法中状态转移方程是用同一行和上一行来更新了
。
比如我猜测当前物品再放一个,我要对比找到max(dp[i][j-w],dp[i-1][j-w])来更新。
。
不过结论不改变
因为你的解法中,前一轮dp等价dp[i-1]。当前轮如果更新了,就是max操作。把dp[i-1][j-w]中的值换成了当前轮的dp[i][j-w]而已


  • 远河梦魂
  • 蔡基
    3
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
@瓦***
我好像是明白一点了。感觉你这条回复对我理解完全背包和多重背包的状态转移方程的本质非常有帮助。
完全背包的状态转移方程本来是
dp[i][j] = max(dp[i - 1][j - b*w] + b*ⅴ for b in [0, ∞) if j - b*w > 0),但它可以进一步分解,变成 dp[i][j] = max(dp[i - 1][j], max(dp[i - 1][j - w - b*w] + ⅴ + b*ⅴ for b in [0, ∞) if j - b*w > 0)),
这个式子最外层max函数的第二个参数和原来的式子是相似的,所以整个式子可以转成dp[i][j] = max(dp[i - 1][j], dp[i][j - w] + ⅴ)。
但对于多重背包,这种相似可能会被打断,比如c = 2时,dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w], dp[i - 1][j - 2w]);但dp[i][j - w]只在j - 3w < 0时,才会等于max(dp[i - 1][j - w], dp[i - 1][j - 2w])。
所以当数量达到上限时,需要有一个“把dp[i - 1][j]放进来,把最前面的dp[i - 1][j - (c+1) * w]拿掉”的操作,也就是队列优化的思路。而我所做的是在数量达到上限后,直接不管状态转移方程了,随便拿了个dp[i][j - 1]进来,只管维持dp[i]单调不减。
那么如果想说明这样做确实是最优解,可能就需要说明在数量可能超限的时候,dp[i][j - 1]就等于max(dp[i - 1][j - b*w] + b*v for b in [1, c](注意这个闭区间是从1开始的,因为0的情况就是dp[i - 1][j]))——在已排序的情况下。
但是自从经由你的回复想到这一点之后,过去三个多点,也仍然没想出个所以然来。
然后你所说的保留i - 1层的信息就可以不用排序,但是我感觉这好像又变成多重背包最原始的思路了,每次都循环遍历dp[i - 1][j - b*w] + b*v for b in [0, c]取最大值(当然这个也可以用我前面提到的队列优化,但是你并没提到队列),时间复杂度和未经二进制优化直接拆成01背包是一样的。也可能我理解错你意思了?


登录百度账号

扫二维码下载贴吧客户端

下载贴吧APP
看高清直播、视频!
  • 贴吧页面意见反馈
  • 违规贴吧举报反馈通道
  • 贴吧违规信息处理公示
  • 12回复贴,共1页
<<返回算法吧
分享到:
©2026 Baidu贴吧协议|隐私政策|吧主制度|意见反馈|网络谣言警示