当前位置: 首页 > 知识库问答 >
问题:

从空间优化的0/1背包实现重构项目列表

顾永福
2023-03-14

0/1背包动态规划算法的空间优化是使用大小等于背包容量的1-d数组(例如A),并在每次迭代i时简单地覆盖A[w](如果需要),其中A[w]表示如果考虑前i个项目并且背包容量为w,则总价值。如果使用这种优化,是否有办法重建选择的项目列表,也许通过在DP算法的每次迭代中保存一些额外信息?例如,在Bellman Ford算法中,可以实现类似的空间优化,并且只要我们保留前身指针的列表,即最后一跳(或第一跳,取决于是否正在编写面向源/目标的算法),最短路径仍然可以重建。

作为参考,这里是我使用动态规划的0/1背包问题的C函数,其中我构造了一个二维向量ans,使得ans[I][j]表示考虑到前I项和背包容量j的总值。我通过反向遍历该向量ans来重建拾取的项:

void knapsack(vector<int> v,vector<int>w,int cap){
 //v[i]=value of item i-1
 //w[i]=weight of item i-1, cap=knapsack capacity
 //ans[i][j]=total value if considering 1st i items and capacity j
 vector <vector<int> > ans(v.size()+1,vector<int>(cap+1));

 //value with 0 items is 0
 ans[0]=vector<int>(cap+1,0);

 //value with 0 capacity is 0
 for (uint i=1;i<v.size()+1;i++){
    ans[i][0]=0;
 }

 //dp
 for (uint i=1;i<v.size()+1;i++) {
    for (int x=1;x<cap+1;x++) {
        if (ans[i-1][x]>=ans[i-1][x-w[i-1]]+v[i-1]||x<w[i-1])
            ans[i][x]=ans[i-1][x];
        else {
            ans[i][x]=ans[i-1][x-w[i-1]]+v[i-1];
        }
    }
 }
 cout<<"Total value: "<<ans[v.size()][cap]<<endl;

 //reconstruction
 cout<<"Items to carry: \n";
 for (uint i=v.size();i>0;i--) {
    for (int x=cap;x>0;x--) {
        if (ans[i][x]==ans[i-1][x]) //item i not in knapsack
            break;
        else if (ans[i][x]==ans[i-1][x-w[i-1]]+v[i-1]) { //item i in knapsack
            cap-=w[i-1];
            cout<<i<<"("<<v[i-1]<<"), ";
            break;
        }
    }
 }
 cout<<endl;
}

共有3个答案

史骏祥
2023-03-14

据我所知,使用建议的解决方案,实际上不可能获得特定目标值的相关项目集。可以通过再次生成丢弃的行或维护适当的辅助数据结构来获得项目集。这可以通过将A中的每个条目与生成该条目的项目列表相关联来实现。然而,这将需要比最初提出的解决方案更多的内存。本文还简要讨论了背包问题的回溯方法。

田兴朝
2023-03-14

虽然这是一个老问题,但我最近遇到了同样的问题,所以我想我应该在这里写下我的解决方案。你需要的是Hirschberg的算法。虽然此算法是为重建编辑距离而编写的,但这里也适用相同的原则。其思想是,当在容量c中搜索n个项目时,在第一次扫描中确定与最终最大值相对应的第(n/2)个项目的背包状态。让我们将此状态称为weight\u m和value\m。这可以通过跟踪大小为c的额外1d数组来实现。因此内存仍然是O(c)。然后将问题分为两部分:容量为weight\m的0到n/2项和容量为c-weight\m的n/2到n项。减少的问题总大小为nc/2。继续这种方法,我们可以确定每个项目后的背包状态(已占用重量和当前值),然后我们可以简单地检查哪些项目被包括在内。该算法在使用O(c)内存的情况下在O(2nc)中完成,因此就大O而言,即使算法的速度至少是原来的两倍,也不会发生任何变化。我希望这对任何面临类似问题的人都有帮助。

尤博达
2023-03-14

下面是yildizkabaran答案的C实现。它改编了赫施伯格的巧妙划分

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

// Returns a vector of (cost, elem) pairs.
vector<pair<int, int>> optimal_cost(vector<int> const& v, vector<int> const& w, int cap) {
    vector<pair<int, int>> dp(cap + 1, { 0, -1 });

    for (int i = 0; i < size(v); ++i) {
        for (int j = cap; j >= 0; --j) {
            if (w[i] <= j && dp[j].first < dp[j - w[i]].first + v[i]) {
                dp[j] = { dp[j - w[i]].first + v[i], i };
            }
        }
    }

    return dp;
}

// Returns a vector of item labels corresponding to an optimal solution, in increasing order.
vector<int> knapsack_hirschberg(vector<int> const& v, vector<int> const& w, int cap, int offset = 0) {
    if (empty(v)) {
        return {};
    }

    int mid = size(v) / 2;
    auto subSol1 = optimal_cost(vector<int>(begin(v), begin(v) + mid), vector<int>(begin(w), begin(w) + mid), cap);
    auto subSol2 = optimal_cost(vector<int>(begin(v) + mid, end(v)), vector<int>(begin(w) + mid, end(w)), cap);

    pair<int, int> best = { -1, -1 };
    for (int i = 0; i <= cap; ++i) {
        best = max(best, { subSol1[i].first + subSol2[cap - i].first, i });
    }

    vector<int> solution;
    if (subSol1[best.second].second != -1) {
        int iChosen = subSol1[best.second].second;
        solution = knapsack_hirschberg(vector<int>(begin(v), begin(v) + iChosen), vector<int>(begin(w), begin(w) + iChosen), best.second - w[iChosen], offset);
        solution.push_back(subSol1[best.second].second + offset);
    }
    if (subSol2[cap - best.second].second != -1) {
        int iChosen = mid + subSol2[cap - best.second].second;
        auto subSolution = knapsack_hirschberg(vector<int>(begin(v) + mid, begin(v) + iChosen), vector<int>(begin(w) + mid, begin(w) + iChosen), cap - best.second - w[iChosen], offset + mid);
        copy(begin(subSolution), end(subSolution), back_inserter(solution));
        solution.push_back(iChosen + offset);
    }

    return solution;
}
 类似资料:
  • 这里的一个明显问题是,对于我所考虑的维度,这种方法消耗的内存将超过可行的(需要空间,是元素数,是最大容量)。进一步研究,我发现提到了(例如,这里也参见Kellerer,Pferschy和Pisinger的“背包问题”)一个内存有效的方法来解决0-1背包。 我们首先将项目集合分成大小大致相等的两个子集。我们将这两个子集视为它们自己的背包问题,给定初始的最大权重,并以节省内存的方式为这两个子集确定最大

  • 我对这个问题有点困惑。作为算法帮助我找到最大的利润从袋。但是这个算法并没有告诉我我应该拿哪一个项目会让我的利润最大。例如 n=4项,背包的容量M=8,利润=[15,10,9,5],重量分别为w=[1,5,3,4],当我解决这个问题时,我得到最大的利润为29 这里是解决方案[http://www.mafy.lut.fi/study/DiscreteOpt/DYNKNAP.pdf] 但是我想知道我应该

  • 我正在为即将到来的测试复习,想知道是否有人能重述问题的b部分。这是从分发的复习表中得到的文本,但我不确定b部分到底在问什么。我更直截了当地猜测“生成一个不到0/1背包问题最优值1%的解”是什么意思。 b)[10pts]举了一个有两个对象的例子,说明对分数背包问题使用的相同的贪婪方法(略加修改,如果贪婪方法选择的最后一个对象不适合,则忽略它)得到的解不到0/1背包问题最优解的1%。

  • 我在写一个背包问题的代码。有一个有重量容量的背包,你选择一个特定的项目组合,以找到最好的解决方案。我试图随机生成可能的解决方案。因此,我的代码将选择随机数量的随机项(生成一个随机大小的列表),并测试解决方案是否可行(小于容量)或不可行(大于容量)。然而,当我试图把所有物品的总重量和总价值相加时,这个数字是关闭的。比如说,这是每一项的数据。 10 27 72 所以值是不正确的。但我看不出我的代码有什

  • 本文向大家介绍C中的0-1背包问题?,包括了C中的0-1背包问题?的使用技巧和注意事项,需要的朋友参考一下 背包是袋子。背包问题是根据物品的值将物品放入袋中的。目的是使袋子内的值最大化。在0-1背包中,您既可以放置物品也可以将其丢弃,没有将物品的某些部分放入背包的概念。 样本问题 重量分布 最大值为65,因此我们将项目2和3放入背包。 0-1背包问题计划 输出结果

  • 我写了一个背包类,我用它来解决背包算法。这个类正在使用动态规划算法来解决这个问题。 我在代码中实现了一些优化,因此我使用线性O(W)空间来找到最大值,但当我试图找到见证时,我仍然需要O(nW)空间来保持布尔表。 有人能告诉我,是否有可能找到背包容量最大、空间较小、复杂度与O(nW)相同的证据,这里W是背包容量。 如果您认为代码中可能还有更多优化,请也告诉他们。