背包问题变体的递归关系?

Posted

技术标签:

【中文标题】背包问题变体的递归关系?【英文标题】:Recurrence relation for a variant of knapsack problem? 【发布时间】:2019-06-05 12:14:42 【问题描述】:

我发现很难理解在 codeforces link 上解决此问题的两个具体实现。

我知道这类似于背包问题。但是,当我自己解决它时,我不知道该算法。我从自己对动态编程的理解中解决了这个问题。我的想法是将丝带的剩余长度视为下一个状态。这是我的代码

#include<iostream>

using namespace std;


int main()
     int n,res1=0,res2,x=0;
     int a,b,c;
     cin >> n >> a >> b >> c;
     for(int i=0;i <= n/a; i++)
        res2 = -20000;
        for(int j=0; j <= (n-(a*i))/b; j++)
             x = (n - (a*i) - (b*j));
            res2=max(res2,(j + ((x % c) ? -10000 : x/c)));
        
            res1=max(res1,i+res2);
     
    cout << res1 << endl;
    return 0;

实施 1:

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 int main()
  4 
  5         int f[4005],n,a,i,j;
  6         fill(f+1,f+4005,-1e9);
  7         cin>>n;
  8         for(;cin>>a;)
  9                 for(i=a;i<=n;i++)
 10                         f[i]=max(f[i],f[i-a]+1);
 11         cout<<f[n];
 12 

实施 2:

  1 #include <bits/stdc++.h>
  2 int n, a, b, c, ost;
  3 std::bitset<4007> mog;
  4 main()
  5 
  6         std::cin>>n>>a>>b>>c;
  7         mog[0]=1;
  8         for (int i=1; i<=n; i++)
  9                 if ((mog=((mog<<a)|(mog<<b)|(mog<<c)))[n])
 10                         ost=i;
 11         std::cout << ost;
 12 

虽然我理解解决背包问题的总体思路。我对实施 1 中的第 8、9、10 行如何实现这一点没有清楚的理解。具体而言,无论 a、b、c 的输入值如何,内部 for 循环都是针对接收到的相应值 a 对数组进行单次传递。

同样,我可以看到实现 2 中的第 8、9、10 行做了同样的事情。但我完全不知道这段代码是如何工作的。

请帮助我理解这一点。我觉得这两种解决方案有一些我没有看到的隐藏结构。提前致谢。

【问题讨论】:

第一个实现非常简单。与您的解决方案相比,它具有倒置的循环顺序:外循环通过a,b,c,内循环设置数组中的所有元素。但我不明白 impl 2 是如何工作的。 【参考方案1】:

实施1

这是非常简单的动态编程实现。

外循环只经过三个值:abc

  8         for(;cin>>a;)

内部循环访问数组的每个元素,并更新当前已知的给定色带长度的最佳切割数。

  9                 for(i=a;i<=n;i++)
 10                         f[i]=max(f[i],f[i-a]+1);

实施 2

我不认为它可以称为动态编程,但技巧相当巧妙。

它分配长度等于最大n 的位数组。然后在左边设置一位。这意味着,长度为 0 的色带是一个有效的解决方案。 在每次迭代中,算法将给定数组向左移动abc。每次这种转变的结果都可以看作是新的有效色带尺寸。通过oring 所有 3 个班次的结果,我们得到i'th 切割后的所有有效尺寸。如果设置了n'th 位,我们知道n 大小的色带可以被切割i 次而没有剩余。

n = 10
a = 2
b = 3
c = 5

i=1:
0|0000000001 // mog
0|0000000100 // mog<<a
0|0000001000 // mog<<b
0|0000100000 // mog<<c
0|0000101100 // mog=(mog<<a)|(mog<<b)|(mog<<c)
^ here is a bit checked in 'if' statement '(mog=(...))[n]'
i=2:
0|0000101100 // mog
0|0010110000 // mog<<a
0|0101100000 // mog<<b
1|0110000000 // mog<<c // here we have solution with two pieces of size 5
1|0111110000 // (mog<<a)|(mog<<b)|(mog<<c)
^ now bit set, so we have a solution

我们知道此时正好有i 剪切,所以我们设置ost=i。但是我们找到了最坏的解决方案,我们必须继续前进,直到确定没有更多解决方案为止。

最终我们会达到这个状态:

i=5:
1|1100000000 // mog
1|0000000000 // mog<<a // 5 pieces of size 2
0|0000000000 // mog<<b
0|0000000000 // mog<<c
1|0000000000 // (mog<<a)|(mog<<b)|(mog<<c)

这是最后一次设置n 位置的位。所以我们将设置ost=5 并进行更多的迭代。

算法使用n 作为可能削减的上限,但很明显这个界限可以改进。例如n / min(a,b,c) 就足够了。

【讨论】:

以上是关于背包问题变体的递归关系?的主要内容,如果未能解决你的问题,请参考以下文章

背包算法的变体

我的背包递归解决方案可以改进吗?

我如何解决 0-1 背包算法的这些变体?

编程竞赛中的背包变体

JavaScript 中的背包变体

背包问题变体 - 将重量和价值最大化到极限