0-1背包

问题描述: 
      给定n种物品和一背包,物品i的重量是wi,其价值为vi,背包的容量为C。问应如何选择装入背包的物品(物品不能分割),使得装入背包中物品的总价值最大? 

抽象描述: 
     x[n]:表示物品的选择,x[i]=1表示选择放进物品i到背包中。 
           
  
 问题分析: 
         1.抽象之后背包问题转换为找到一个最优的数组,x1,x2,.....,xn的0-1序列。 
         
         2.假设最优解的序列为x1,x2,.....,xn,能使背包容量C的总价值最大. 
               如果,x1=1,则x2,...,xn是C-w1容量的背包的总价值依然是 最大的序列; 
               如果,x1=0,则x2,....,xn是C容量的背包的总价值依然是 最大的序列。 
           这就是我们所说的最优子结构性质。 
         
         3.进一步分析:我们用m(i,j)表示为已经判断好了i:n的序列的背包最大价值,并且此时的背包剩余的容量为j,对物品i进行判断 
                如果j>wi, 就只要做出选择wi和不选择wi情况下,哪种更能使背包的总价值更大:m(i,j)=max{ m(i+1,j),m(i+1,j-wi)+vi}(注意这是个递归式) 
                如果j<wi:       m(i,j)=m(i+1,j) 
                初始化:        m(n,j)=vn  (j>= wn); 
                                m(n,j)=0   (0<=j< wn) 
                                m(0,C)=0    
             最终的结果:m(1,C) 

        4.依次我们就得到了一个递归的表达式: 
                 
       
       5.如果单纯的从利用递归,重复计算了很多的值,耗费的时间是很大的,动态规划还需避免这种重复计算,怎样自顶向下或自底向上的计算呢? 
          采用列表的方法就可以很好的分析设计自顶向下或自底向上的计算的算法了 

 举例分析: 
         n=3,c=6,w={4,3,2} v={5,2,1} 
         m[i][j]=max{ m[i+1][j], m[i+1][j-w[i]]+v[i] } 
         列表如下: 

         

          最左边箭头:我们计算的方向,从第3行开始向上计算法值。 
          表中红色箭头是我们通过选择做出的结果:列如 m[2][3]=max{m[3][3],m[3][3-w[2]]+v[2]},我们最终选择了m[3][3-w[2]]+v[2]。 
          整个问题的最优解保存在m[1][6]中。


#include <iostream>
#include <vector>
using namespace std;

int maxPrice(int n, int c, vector<int> vecw, vector<int> vecp);
int main()
{
    //物品的数量和背包的容量
    int n, c, weight, price;

    //获得n个物体的重量和价值,n,c都为0的时候结束
    cin >> n >> c;
    while( n != 0 && c != 0 )
    {
        // 物品的重量vecw和物体的价值vecp
        vector<int> vecw, vecp;
        for (int i = 0; i < n; ++i)
        {
            cin >> weight;
            vecw.push_back(weight);
        }
        for (int j = 0; j < n; ++j)
        {
            cin >> price;
            vecp.push_back(price);
        }

        //找到价值的最大值
        int max_Price = maxPrice(n, c, vecw, vecp);
        cout << max_Price << endl;
        cin >> n >> c;
    }

    return 0;
}

int maxPrice(int n, int c, vector<int> vecw, vector<int> vecp)
{
    int price = 0, flag = 0;

    if (c == 0)
        return 0;
    for(int l = 0; l < n; ++l)//测试是否有能装入包中的数据
        if (vecw[l] < c)
        {
            flag = 1;
            break;
        }
    if (flag == 0)  //如果没有能装入包中的数据就返回 0
        return 0;

    for(int i = 0; i < n; ++i)
    {
        int weight = c;
        weight -= vecw[i];
        if (weight >= 0)
        {
            vector<int> vec_w, vec_p;
            for (int j= 0; j < n; ++j)
                if ( i != j)//创建一个新的数组存储下一组数据
                {
                    vec_w.push_back(vecw[j]);
                    vec_p.push_back(vecp[j]);
                }

            int current =  vecp[i] + maxPrice(n-1, weight, vec_w, vec_p);//递归求最大值
            if ( price < current)
                price = current;
        }
    }
    return price;
}

运行结果:

 

本文转自博客园xingoo的博客,原文链接:0-1背包,如需转载请自行联系原博主。

时间: 2024-09-27 08:11:09

0-1背包的相关文章

poj 1976 A Mini Locomotive(0/1背包)

点击打开链接poj 1976 思路: 0/1背包 分析: 1 题目给定n个数,要求三段连续的m个数之和最大 2 假设n个数是num[1],num[2],num[3]......num[n],那么m个连续的数可能有n-m+1种可能即num[1]+num[2]...+num[k] , num[2]+num[3]...+num[k]...... 3 我们令v[1] = num[1]+num[2]...+num[k] , v[2] = num[2]+num[3]...+num[k].那么总的有n-m+1

hdu 2955 Robberies(0/1背包)

点击打开链接hdu 2955 思路: 0/1背包 分析: 1 按照题目的意思我们很容易知道这就是一个0/1背包问题,如果我们把概率当作是背包的容量,那么我们遇到一个问题就是浮点数的dp,因为题目没有告诉我们小数点具体几位,那么我们就不能够通过乘上10^n来转化为整数,所以我们应该考虑换种思想 2 按照正常的思路是dp[i][j]表示前i个物品放入概率为j的最大价值,那么我们这边把价值当成背包来看的话我们设dp[i][j]表示前i个物品放入金钱为j的最小被抓概率(因为题目不好求最小概率我们可以认为

poj 1948 Triangular Pastures(二维0/1背包)

点击打开链接poj 1948 思路: 二维0/1背包 分析: 1 题目要求从n个木棒里面选出m个组成一个三角形,使得三角形的面积最大 2 对于三角形来说知道了两条边和周长就可以求面积,按照0/1背包的思想dp[i][j][k]表示前i个木棒能否组成第一条边为长度j,第二条长度为k.如果dp[j-v[i]][k] 或dp[j][k-v[i]] 为true则dp[j][k]就为true 3 我们求出了所有可能的组合之和,就去枚举所有的边长的情况,然后求最大的面积 代码: #include<cmath

hdu 3466 Proud Merchants(0/1背包)

点击打开链接hdu 3466 思路: 0/1背包 分析: 1 这一题和"hdu2546 饭卡"有点像,但是又有不同,不同的是这一题每一个物品都有一个限制的值Q[i],求最大的价值 2 题目明确提到每一种物品只能卖一次或这不卖,那这明显就是0/1的性质,但是现在多了一个条件钱不能少于Q[i].这边的话我各种YY无果之后,果断看了题解,发现是要按照q-p排序,然后再做dp. 3 经过一番的YY之后,我明白了为什么按照q-p是正确的.我们都知道dp有一个很重要的特点就是无后效性,如果没有金钱

poj 3624 Charm Bracelet (0/1背包)

点击打开链接 裸0/1背包 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int MAXN = 15000; int n , m; int w[MAXN] , v[MAXN] , dp[MAXN]; int main(){ while(scanf("%d%d" , &n , &

hdu 2639 Bone Collector II (0/1背包)

点击打开链接hdu 2639 思路: 0/1背包求第k优解 分析: 1 其基本思想是,将每个状态都表示成有序队列,将状态转移方程中的 max/min 转 化成有序队列的合并. 2 这里仍然以 01 背包为例讲解一下.    首先看 01 背包求最优解的状态转移方程: F [i, v] = max{F [i − 1, v], F [i − 1, v −Ci ] + Wi }.如果要求第 K 优解,那么状态 F [i, v] 就应该是一个大小为 K 的队列F [i, v, 1 . . . K].其中

uva 562 Dividing coins (0/1背包)

点击打开链接uva 562 思路: 0/1背包 分析: 1 题目意思是有两个人分n个金币,要求最后两个人的金币差值最小 2 我们利用背包的思想即背包的总容量为金币总和的一半,去求出可以放入背包的最大的金币(这里其实就是某一个人能获得的最大的金币),那么最后的ans就是(sum-dp[sum/2])-dp[sum/2] 代码: #include<cstdio> #include<cstring> #include<iostream> #include<algorit

poj 3211 Washing Clothes(0/1背包)

点击打开链接poj 3211 思路: 0/1背包 分析: 1 题目要求洗完n件衣服所需的最少的时间,并且必须洗完一种颜色才能跳到下一种 2 仔细想想这一题和uva上面的一道分金币非常的相似,由于要求必须洗完一种颜色才能跳到下一种,那么我们只要使得洗每一种颜色的时间最少那么总的就最少,那怎样才能够最少的时间呢?也就是要求两个人的时间差最小(也就相当于一个人用一半的时间去能够洗的最多的衣服),那么就转化为0/1背包的思想 3 做m次的dp然后求和即可 代码: #include<cstdio> #i

hdu 2126 Buy the souvenirs(二维0/1背包)

点击打开链接hdu2126 思路: 二维0/1背包 分析: 1 题目给定n个物品的价钱和m的钱,问最多能买到的物品数有几种方案. 2 很明显就可以写出状态转移方程dp[i][j][k]表示的是前i个物品选j个总价钱为k的方案数 那么dp[i][j][k] = dp[i-1][j][k]+dp[i-1][j-1][k-v[i]].由于都可以把第一维去掉,所以正常的情况下直接写出dp[j][k] = dp[j][k] + dp[j-1][k-v[i]] 代码: #include<cstdio> #

hdu 2546 饭卡(0/1背包)

点击打开链接hdu 2546 思路: 贪心+0/1背包 分析: 1 题目中收到只有当余额大于等以5的时候才可以买东西,那么我们利用m-5去买这样保证了于额不会小于5,那么这样就可以买东西了,因为最后可能还有点于额(为0)的时候也是可以买的,那么最后一次就买最贵的这样保证于额最少了. 2 这边我们用到贪心+dp的思想,总体不好想,对于我这种dp弱逼来说没有题解我真不懂(有了题解也不懂) 代码: #include<cstdio> #include<cstring> #include&l