动态规划7_0-1背包

接下来是动态规划里的经典问题----背包问题

这小节讲得是0-1背包(ZeroOnePack)。


问题模型:

给定一个载重量为m,n个物品,其重量为wi,价值为vi,1<=i<=n,要求:把物品装入背包,每个物品都只有一件, 并使包内物品价值最大。

题目的要求是

(1)每种物品只有一个,要么取,要么不取。(这就是为何叫0-1背包 0就是不取,1就是取 只有这两种状态)

(2)背包有容量的大小,最多只能装重量为m的物品(不一定要装满)。

(3)使得物品价值最大。


很明显直接按(价值)贪心是不可行的。

例如m=105;

v={20,15,1,5}

w={100,10,10}

如果按照贪心去做,得到的是20,然而最优解是15+15.


然后尝试从取和不取这种思想去递归求解。

int solve(int W,int i)
{
  if(i==n)
	  return 0;
  if(w[i]>W)
  {
	  return  solve(W,i+1);
  }
  else 
  {
	  return max(solve(W-w[i],i+1)+v[i],solve(W,i+1));
  }
}


我们可以得知上面的函数的复杂度为O(2^n)。是个指数级的复杂度,适用范围很小,当n很大时,程序会耗费很多时间。


我们会发现,上面函数中会重复算一下子问题。

所以我们可以将计算出来的结果记录下来,当需要用时直接返回即可(也称为记忆化搜索)。

int rec(int i,int j)
{
	if(dp[i][j]>=0)
		return dp[i][j];
	
	if(i==n)
		return 0;
	else if(j<w[i])
	{
		return rec(i+1,j);
	}
	else 
		return max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);
}
void solve()
{
	//初始化为负值,代表未被计算过
	memset(dp,-1,sizeof(dp));
	printf("%d\n",rec(0,W));
}

这样将复杂度降低到O(nW)。即是参数的组合的个数。


最后是动态规划的思想,从前面的分析可得,


if(j<w[i])

dp[i+1][j]=dp[i][j] 

else 

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

void solve()
{
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<=W;j++)
		{
			if(j<w[i])	//不能取
				dp[i+1][j]=dp[i][j];
			else		//不取  和  取   的最大值 
				dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]);
		}
	}
}

运行dp数组的变化:

图片摘自:http://www.cnblogs.com/fly1988happy/archive/2011/12/13/2285377.html




if(j<w[i])

dp[i+1][j]=dp[i][j] 

else 

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

从状态转移方程可以看出计算dp[i+1][]时只用到dp[i]。所以可以将其空间从O(nW)优化成O(W)。

void solve()
{
	for(int i=0;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]);
}


第二个循环逆序表示dp[j]和dp[j-c[i]]+w[i]是前一个状态的


最后是时间复杂度常数级优化(当W比较大时有用)

由于只需要最后dp[W]的值,倒推前一个物品,其实只要知道dp[W-w[n]]即可。以此类推,

对以第i个背包,其实只需要知道到dp[W-sum{w[i..n]}]即可,即代码中的

for i=1..N 

   for v=V..w[i]

可以改成

for i=1..n 

b=max{V-sum{w[i..n]},c[i]} 

for v=V..b

这对于V比较大时是有用的。

代码实现:

	memset(dp,0,sizeof(dp));
	memset(s,0,sizeof(s));
	for(int i=1;i<=n;i++)
	{
		scanf("%d %d",&v[i],&w[i]);
		s[i]=s[i-1]+w[i];
	}
	for(int i=1;i<=n;i++)
	{
		int b=max(w[i],W-s[n]+s[i-1]);
		for(int j=W;j>=b;j--)
			dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
	}
	printf("%d\n",dp[W]);

推荐题目: HDU2602    HDU1864


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值