01背包问题—动态规划

一、问题描述

简述

试设计一个用回溯法搜索子集空间树的函数。该函数的参数包括结点可行性判定函数和上界函数等必要的函数,并将此函数用于解0-1背包问题。 0-1 背包问题描述如下:给定n 种物品和一个背包。物品i 的重量是wi ,其价值为vi ,背包的容量为C。应如何选择装入背包的物品,使得装入背包中物品的总价值最大? 在选择装入背包的物品时,对每种物品i只有2 种选择,即装入背包或不装入背包。不能将物品i 装入背包多次,也不能只装入部分的物品i。

输入

第一行有2个正整数n和c。n是物品数,c是背包的容量。接下来的1 行中有n个正整数,表示物品的价值。第3 行中有n个正整数,表示物品的重量。

输出

将计算出的装入背包物品的最大价值和最优装入方案输出。第一行输出为:Optimal value is

Sample Input

5 10
6 3 5 4 6
2 2 6 5 4

Sample Output

Optimal value is
15
1 1 0 0 1

二、解题思路

动态规划基本思想

如果能够保存已解决的子问题的答案,而在需要时再找出以求得的答案,就可以避免大量重复计算,从而得到多项式时间算法。为了达到这个目的,可以用一个表来记录所有已解决子问题的答案。不管子问题以后是否被用到,只要他被计算过就将其结果填入表中。

证明问题满足贪心选择性质和最优子结构性质

核心代码

m[i][j] = max(m[i-1][j], m[i-1][j-w[i]] + v[i]);

  • m[i][j]表示背包容量为j,可选择的物品为前j个物品时,01背包问题的最优值。
  • 第i个物品是否加入背包取决于:这个物品的价值 v[i] 加上背包加入该物品后剩余空间能装物品的价值 m[i-1][j-w[i]] 与不加该物品背包空间能装物品的价值 m[i-1][j] 谁大。
  • 由于是从第一个物品开始求,所以每个物品不加前的最优解都知道答案。
  • traceback()函数中也是通过比较 m[i][j]m[i-1][j] 是否相等来判断第i个物品是否被装进背包。

三、代码

C++

#include <iostream>

using namespace std;

const int N = 1000;

int v[N], w[N], x[N];
int m[N][N];
int c=0, n=0;
void traceback();
int main(){
    cin>>n>>c;
    for (int i = 1; i <= n; ++i) {
        cin>>v[i];
    }
    for (int i = 1; i <= n; ++i) {
        cin>>w[i];
    }

    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=c;j++)
        {
           if(j>=w[i]){
               m[i][j] = max(m[i-1][j], m[i-1][j-w[i]] + v[i]);
           }
           else
               m[i][j] = m[i-1][j];
        }
    }
    traceback();

    cout<<"Optimal value is"<<endl;
    cout<<m[n][c]<<endl;
    for(int i=1;i<n;i++)
        cout<<x[i]<<' ';
    cout<<x[n]<<endl;
    return 0;

}
void traceback(){
    int k=c;
    for (int i = n; i > 0; i--) {
        if(m[i][k] == m[i-1][k])
            x[i]=0;
        else{
            x[i]=1;
            k-=w[i];
        }
    }
}
posted @ 2020-08-11 13:19  陈葛杰  阅读(343)  评论(0)    收藏  举报