01背包与物品无限背包

一、01背包问题

01背包是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为C1,C2,…,Cn,与之相对应的价值为W1,W2,…,Wn.求解将那些物品装入背包可使总价值最大。

动态规划:

1) 子问题定义:F[i][j]表示前i件物品中选取若干件物品放入剩余空间为j的背包中所能得到的最大价值。

2) 根据第i件物品放或不放进行决策

其中F[i-1][j]表示前i-1件物品中选取若干件物品放入剩余空间为j的背包中所能得到的最大价值;

而F[i-1][j-C[i]]+W[i]表示前i-1件物品中选取若干件物品放入剩余空间为j-C[i]的背包中所能取得的最大价值加上第i件物品的价值

根据第i件物品放或是不放确定遍历到第i件物品时的状态F[i][j]。

设物品件数为N,背包容量为V,第i件物品体积为C[i],第i件物品价值为W[i]。

由此写出伪代码如下:

 1      F[0][] ← {0}
 2
 3      F[][0] ← {0}
 4
 5      for i←1 to N
 6
 7          do for k←1 to V
 8
 9              F[i][k] ← F[i-1][k]
10
11              if(k >= C[i])
12
13                  then F[i][k] ← max(F[i][k],F[i-1][k-C[i]]+W[i])
14
15      return F[N][V]

上述代码9-13行也可以这样写:

if(k<C[i])//如果第i个物品的体积已经大于背包剩余容量
        then   F[i][k] ← F[i-1][k]
else    F[i][k] ← max(F[i-1][k],F[i-1][k-C[i]]+W[i])

理解为:当第i个物品的体积大于背包剩余容量时,第i个物品肯定不能被放进去,所以F[i][k]=F[i-1][k],

而当第i个物品的体积小于或等于背包剩余容量时,可以有两种选择,放第i个物品(F[i-1][k-C[i]]+W[i])或者不放(F[i-1][k]),然后选择两者之间最优的。

而写成第一种等价的伪代码是为了更好的理解从二维数组解法到一维数组解法的转换。

根据算法求出的最大价值表本身其实含有位置信息,从F[N][V]逆着走向F[0][0],设i=N,j=V,如果F[i][j]==F[i-1][j-C[i]]+W[i]说明包里面有第i件物品,同时j -= C[i],不管F[i][j]与F[i-1][j-C[i]]+W[i]相不相等i都要减1,因为01背包的第i件物品要么放要么不放,不管放还是不放其已经遍历过了,需要继续往下遍历。

打印背包内物品的伪代码如下:

 1      i←N
 2
 3      j←V
 4
 5      while(i>0 && j>0)
 6
 7          do if(F[i][j]=F[i-1][j-C[i]]+W[i])
 8
 9              then Print W[i]
10
11                   j←j-C[i]
12
13          i←i-1

也可以定义一个二维数组Path[N][V]来存放背包内物品信息,开始时Path[N][V]初始化为0,当 F[i][j]==F[i-1][j-C[i]]+W[i]时Path[i][j]置1。最后通过从Path[N+1][V+1]逆着走向Path[0][0]来获取背包内物品。其中Path[0][]与Path[][0]为边界。

加入路径信息的伪代码如下:

     F[0][] ← {0}

     F[][0] ← {0}

     Path[][] ← 0

     for i←1 to N

         do for k←1 to V

             F[i][k] ← F[i-1][k]

             if(k >= C[i] && F[i][k] < F[i-1][k-C[i]]+W[i])

                 then F[i][k] ← F[i-1][k-C[i]]+W[i]

                      Path[i][k] ← 1

     return F[N][V] and Path[][]

打印背包内物品的伪代码如下:

     i←N

     j←V

     while(i>0 && j>0)

         do if(Path[i][j] = 1)

             then Print W[i]

                  j←j-C[i]

         i←i-1

将使用二位数组改为使用一维数组:

观察伪代码可也发现,F[i][j]只与F[i-1][j]和F[i-1][j-C[i]]有关,即只和i-1时刻状态有关,所以我们只需要用一维数组F[]来保存i-1时的状态F[]。假设i-1时刻的F[]为{a0,a1,a2,…,av},难么i时刻的F[]中第k个应该为max(ak,ak-C[i]+W[i])即max(F[k],F[k-C[i]]+W[i]),这就需要我们遍历V时逆序遍历,这样才能保证求i时刻F[k]时F[k-C[i]]是i-1时刻的值。如果正序遍历则当求F[k]时其前面的F[0],F[1],…,F[K-1]都已经改变过,里面存的都不是i-1时刻的值,这样求F[k]时利用F[K-C[i]]必定是错的值。最后F[V]即为最大价值。

求F[j]的状态方程如下:

伪代码如下:

1      F[] ← {0}
2
3      for i ← 1 to N
4
5          do for k ← V to C[i]
6
7              F[k] ← max(F[k],F[k-C[i]]+W[i])
8
9      return F[V]

加入路径信息的伪代码如下:

 1      F[] ← {0}
 2
 3      Path[][]←0
 4
 5      for i←1 to N
 6
 7          do for k←V to C[i]
 8
 9             if(F[k] < F[k-C[i]]+W[i])
10
11                  then F[k] ← F[k-C[i]]+W[i]
12
13                       Path[i][k] ← 1
14
15      return F[V] and Path[][]

二、物品无限的背包问题

将01背包一维数组解法中j的遍历顺序do for k←V to C[i]改为do for k←C[i] to V就变成了物品无限背包的解法。

1      F[] ← {0}
2
3      for i ← 1 to N
4
5          do for k ← C[i] to V
6
7              F[k] ← max(F[k],F[k-C[i]]+W[i])
8
9      return F[V]

三、代码实际操练:

二维数组解法:

01背包问题具体例子:先输入两个数n,V表示物品的个数和背包的容量,接下来输入n组数据代表n种物品,每组数据有两个值对应物品的体积和价值,每种物品只有一个,求在背包容量下能装物品最大价值,并求出最大价值下,组合中各个物品的价值?

#include<iostream>
using namespace std;
const int N_max=100;//物品个数上限
const int V_max=100;//背包容量上限
int dp[N_max][V_max];
bool flag[N_max][V_max];
int main()
{
    int n;//实际输入物品的个数
    cin>>n;
    int V;//背包的实际容量
    cin>>V;
    int *v=new int[n+1];
    int *price=new int[n+1];
    //输入n组数据,分别为体积v和价值price
    //注意这里要从1开始
    for(int i=1;i<=n;i++)
    {
        cin>>v[i]>>price[i];
    }
    //初始化dp数组
    //注意这里的=
    for(int i=0;i<=n;i++)
    {
        dp[i][0]=0;
    }
    for(int i=0;i<=V;i++)
    {
        dp[0][i]=0;
    }
    //初始化flag数组
    memset(flag,false,(n+1)*(V+1)*sizeof(bool));
    //开始递推
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=V;j++)
        {
            dp[i][j]=dp[i-1][j];
            if(v[i]<=j && dp[i-1][j-v[i]]+price[i]>dp[i][j])//放下该物品
            {
                dp[i][j]=dp[i-1][j-v[i]]+price[i];
                flag[i][j]=true;
            }
        }
    }
    cout<<"能容下的最大价值是:"<<dp[n][V]<<endl;
    cout<<"组成最佳值的物品价值如下:"<<endl;
    int i=n;
    int j=V;
    while(i>=0 && j>=0)
    {
        if(flag[i][j])
        {
            cout<<price[i]<<endl;
            j=j-v[i];
        }
        i--;
    }
    return 0;
}

一维数组的解法:

#include<iostream>
using namespace std;
bool flag[100][100]={false};
int main()
{
    int n;//实际输入物品的个数
    cin>>n;
    int V;//背包的实际容量
    cin>>V;
    int *dp=new int[V+1];
    int *v=new int[n+1];
    int *price=new int[n+1];
    //输入n组数据,分别为体积v和价值price
    //注意这里要从1开始
    for(int i=1;i<=n;i++)
    {
        cin>>v[i]>>price[i];
    }
    //初始化dp数组
     memset(dp,0,(V+1)*sizeof(int));
    //开始递推
    for(int i=1;i<=n;i++)
    {
        for(int j=V;j>=v[i];j--)
        //for(int j=v[i];j<=V;j++)
        {
            if( dp[j-v[i]]+price[i]>dp[j])//放下该物品
            {
                dp[j]=dp[j-v[i]]+price[i];
                flag[i][j]=true;
            }
        }
    }

    cout<<"能容下的最大价值是:"<<dp[V]<<endl;
    cout<<"组成最佳值的物品价值如下:"<<endl;
    int i=n;
    int j=V;
    while(i>=0 && j>=0)
    {
        if(flag[i][j])
        {
            cout<<price[i]<<endl;
            j=j-v[i];
        }
        i--;
    }
    return 0;
}

调试结果:

物品无限背包问题具体例子:先输入两个数n,V表示物品的个数和背包的容量,接下来输入n组数据代表n种物品,每组数据有两个值对应物品的体积和价值,每种物品有无限个,求在背包容量下能装物品最大价值,并求出最大价值下,组合中各个物品的价值?

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
bool flag[100][100]={false};
int main()
{
    int n;//实际输入物品的个数
    cin>>n;
    int V;//背包的实际容量
    cin>>V;
    int *dp=new int[V+1];
    int *v=new int[n+1];
    int *price=new int[n+1];
    //输入n组数据,分别为体积v和价值price
    //注意这里要从1开始
    for(int i=1;i<=n;i++)
    {
        cin>>v[i]>>price[i];
    }
    //初始化dp数组
     memset(dp,0,(V+1)*sizeof(int));
    //开始递推
    for(int i=1;i<=n;i++)
    {
        for(int j=v[i];j<=V;j++)
        {
            if( dp[j-v[i]]+price[i]>dp[j])//放下该物品
            {
                dp[j]=dp[j-v[i]]+price[i];
                flag[i][j]=true;
            }
        }
    }

    cout<<"能容下的最大价值是:"<<dp[V]<<endl;

    return 0;
}

时间: 2024-10-16 01:30:54

01背包与物品无限背包的相关文章

RQNOJ 671 纯洁的买卖:无限背包

题目链接:https://www.rqnoj.cn/problem/671 题意: ALEJ要通过倒卖东西来赚钱. 现在他有m元经费. 有n种物品供他选择,每种物品数量无限. 第i件物品的买入价为c[i],卖出价为r[i],每卖出一件物品i后,要交c[i]的税. 问:一次买卖之后,经费最多有多少. 题解: 注:(1)"买"和"卖"是有顺序的. 也就是说,收购一件物品所得到的"未来利润"并不能当作现在的经费来用. (2)"缴税"

01背包模板、全然背包 and 多重背包(模板)

转载请注明出处:http://blog.csdn.net/u012860063 贴一个自觉得解说不错的链接:http://www.cppblog.com/tanky-woo/archive/2010/07/31/121803.html 模版就直接贴代码: 01背包模板: /* 01背包问题 01背包问题的特点是,">每种物品仅有一件.能够选择放或不放. 01背包问题描写叙述: 有N件物品和一个容量为V的背包. 第i件物品的重量是c[i],价值是w[i]. 求解将哪些物品装入背包可使这些物品

【转】 背包问题——“完全背包”详解及实现(包含背包具体物品的求解)

完全背包是在N种物品中选取若干件(同一种物品可多次选取)放在空间为V的背包里,每种物品的体积为C1,C2,…,Cn,与之相对应的价值为W1,W2,…,Wn.求解怎么装物品可使背包里物品总价值最大. 动态规划(DP): 1) 子问题定义:F[i][j]表示前i种物品中选取若干件物品放入剩余空间为j的背包中所能得到的最大价值. 2) 根据第i种物品放多少件进行决策      (备注:应该还有一项f[i-1][j])        (2-1) 其中F[i-1][j-K*C[i]]+K*W[i]表示前i

hdoj 1203 I NEED A OFFER! 【另类01背包】【概率背包】

题意:... 策略:动态规划. 因为是求至少能得到一个offer的概率,那我们可以反着求,求得不到一个offer的概率,最后用1减去就好了. 代码: #include<string.h> #include<stdio.h> double dp[10010]; struct node{ int a; double b; }s[10010]; int main() { int n, m, i, j; while(scanf("%d%d", &n, &

多重背包转换成完全背包和01背包

void CompletePack(int cost,int weight)   多重背包 { for(int i=cost;i<=m;i++) dp[i]=max(dp[i],dp[i-cost]+weight); } void ZeroOnePack(int cost,int weight)    01背包 { for(int i=m;i>=cost;i--) dp[i]=max(dp[i],dp[i-cost]+weight); } void MultiplyPack(int cost,

projecteuler----&gt;problem=31----Coin sums 无限背包计算可能存在的次数

Problem 31 In England the currency is made up of pound, £, and pence, p, and there are eight coins in general circulation: 1p, 2p, 5p, 10p, 20p, 50p, £1 (100p) and £2 (200p). It is possible to make £2 in the following way: 1×£1 + 1×50p + 2×20p + 1×5p

nyoj 311-完全背包 (动态规划, 完全背包)

311-完全背包 内存限制:64MB 时间限制:4000ms Special Judge: No accepted:5 submit:7 题目描述: 直接说题意,完全背包定义有N种物品和一个容量为V的背包,每种物品都有无限件可用.第i种物品的体积是c,价值是w.求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大.本题要求是背包恰好装满背包时,求出最大价值总和是多少.如果不能恰好装满背包,输出NO 输入描述: 第一行: N 表示有多少组测试数据(N<7). 接下来每组测试数

ACM:动态规划,物品无限的背包问题(完全背包问题)

题目:有n种物品,每种物品都有无限件可用.第i种物品的体积是vi,重量是wi.选一些物品装到一个容量为C的背包中,使得背包内物品在总体积不超过C的前提下重量尽量大. 分析,完全背包问题,相对于上上篇文章的硬币问题,只是由DAG上的无权图变成了这里的DAG上的带权图! 输出最后满足体积不超过背包容量的条件下,背包中的最大重量. 代码: #include <iostream> #include <string> using namespace std; const int MAXN =

hdu 3236 Gift Hunting 01背包中有两个背包有两种物品+1次的免费券

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3236 这道题目属于那种 一看:吓尿了 再看:诶 可以做 一做:跪的不行不行的... 两个背包 相当于二维花费 但是每次只需要花1种 这次我的dp数组从一开始就想对了 dp[j][k][i]表示第一个背包装了j且第二个背包装了k且用了(1-i)张免费券后能得到的最大的开心值 我参考了dango的做法 先算把特殊物品取了 在这个过程中得到三维dp数组的所有合法状态[如果特殊物品没取完 那么结果是dp数组