0-1背包问题

0-1背包问题

有n件物品,第i件物品(I = 1,2,3…n)的价值是vi, 重量是wi,我们有一个能承重为m的背包,我们选择一些物品放入背包,显然放入背包的总重量不超过m。我们要求选择物品的总价值最大,请问如何选择?这里我们假设所有出现的数都是正整数。

输入

第1行,2个整数,N和W中间用空格隔开。N为物品的数量,W为背包的容量。(1 <= N <= 100,1 <= W <= 10000)
第2 - N + 1行,每行2个整数,Wi和Pi,分别是物品的体积和物品的价值。(1 <= Wi, Pi <= 10000)

输出

输出可以容纳的最大价值。

输入示例

3 6
2 5
3 8
4 9

输出示例

14

分析(转摘):

   

(1) 枚举。但对于n件物品,每件都可以选择取或者不取,总的可能性有2n, n = 30就大约已经有10亿种可能了!枚举所有可能选择一种不超过背包承重并且价值最大的物品组合,枚举量太大了。

(2) 贪心。 先选最贵重的物品?找个反例:

n = 3, m = 3
v = (2,2,3)
w = (1,2,3)

按照先选贵重物品的策略,会先选择价值为3的那个,并且背包装满了,但是如果我们选取前两个物品,总价值可以达到4。
可能你已经想到了,考虑“性价比”,先选取“性价比”高的。性价比怎么定义呢?用价值除以重量!先选取单位重量价值最大的物品试试?

再举个例子:

n = 3, m = 7
v = (2,3,4)
w = (3,4,5)

按我们的方法因为2/3 <  3/4 < 4 / 5,我们先选择第三件物品,但是选了它之后别的东西放不下了!总价值是4,但如果我们选择前两件物品可以拿到总价值5。可见这两种贪心法是不行的。

动态规划:

令f(i,j)是决定了前i件物品,总价值恰好是j时的最小重量,那么经过类似的分析,我们可以写出这样的初值和递推式:


  f(i,j)=0(i=0j=0)(i=0j>0)f(i1,j)(i>0j<vi)max(f(i1,j),f(i1,jwi)+vi)(i>0j>vi)  

代码:

   二维数组(未优化):

     

#include <iostream>
#include <cstdio>
#include <cstring> 
#include <algorithm>
using namespace std;
int w[110],v[110];
int dp[110][10010];//dp[i][j] 表示选决定了前i件物品,重量恰好为j的时候能获得的最大价值。 
int main()
{   
    int n,W;
    scanf("%d %d",&n , &W);
    for(int i = 1; i <= n; i++)
      {
      	   scanf("%d %d",&w[i],&v[i]);
	  }
	  memset(dp , 0 ,sizeof(dp));
	  for(int i = 1; i <= n; i++) 
	    {
	    	 for(int j = 0; j < w[i] && j < W; j++)
	    	  { 
			  dp[i][j] = dp[i-1][j];
	    	  //printf("===%d==%d==%d===\n",i,j,dp[i][j]); 
			  }                     //不选第i件物品时 
	    	for(int j = w[i]; j <= W ; j++) //选第i件物品时 
	    	  {
			  dp[i][j] = max(dp[i-1][j] , dp[i-1][j-w[i]]+v[i] );
	    	//printf("===%d==%d==%d===\n",i,j,dp[i][j]); 
			}
		}
	  printf("%d\n",dp[n][W]); 
	  
	return 0;
}

   一维数组(优化):

    

#include <iostream>
#include <cstdio>
#include <cstring> 
#include <algorithm>
using namespace std;
int w[110],v[110];
int dp[10010]; 
int main()
{   
    int n,W;
    scanf("%d %d",&n , &W);
    for(int i = 1; i <= n; i++)
      {
      	   scanf("%d %d",&w[i],&v[i]);
	  }
	  memset(dp , 0 ,sizeof(dp));
	  for(int i = 1; i <= n; i++) 
	    {
           for(int j = W ; j >= w[i]; j--)
            
            	 dp[j] = max(dp[j] , dp[j - w[i]] + v[i]);
			}
	  printf("%d\n",dp[W]); 
	  
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值