[算法]动态规划 多重背包 二进制优化

/*
Name:多重背包 二进制优化 (DP)

Actor:HT

Time:2015年11月2日

Error Reporte:

*/

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <vector>
#include <stack>
#include <map>
#include <string>

#define N 10010

using namespace std;

int dp[100];
int value[N];
int cost[N];
int size[N];	//数量

int n, m;	//n个宝石 m大小背包

int main()
{
	int t, i, j, k;
	int temp;
	while (scanf("%d %d", &n,&m) != EOF)
	{
		for (i = 0; i < n; i++)
		{
			scanf("%d %d %d", &value[i], &cost[i], &size[i]);
		}
		for (i = 0; i < m; i++)
		{
			dp[i] = 0;		//初始化
		}

		for (i = 0; i < n; i++)
		{
			if (size[i] * cost[i] >= m)		//如果个数足够多,当做完全背包处理
			{
				for (j = cost[i]; j <= m; j++) 
				{
					dp[j] = (dp[j]>dp[j - cost[i]] + value[i]) ? dp[j] : dp[j - cost[i]] + value[i];
				}
			}
			else							//不够多,那就二进制拆成01背包吧
			{
				int div1, div2;		//div1是已经看了的小堆(1、2、4...),div2是剩余所有量
				div1 = 1;
				div2 = size[i];
				while (div1 < div2)
				{
					for (j = m; j >= div1*cost[i]; j--)
					{
						dp[j] = dp[j]>dp[j - div1*cost[i]] + div1*value[i] ? dp[j] : (dp[j - div1*cost[i]] + div1*value[i]);
					}
					div2 -= div1;
					div1 *= 2;
				}
				for (j = m; j >= div2*cost[i]; j--)	//剩余的量再补一次
				{
					dp[j] = dp[j]>dp[j - div2*cost[i]] + div2*value[i] ? dp[j] : (dp[j - div2*cost[i]] + div2*value[i]);
				}
			}
		}

		printf("%d\n", dp[m]);
	}
	return 0;
}

//dp[j] = (隐含:看了i个宝石,)背包剩余空间j
//dp[j] = max{dp[j] , dp[j-c[i]] + v[i]}


//后记:二进制思想

//假设有1000个苹果,现在要取n个苹果,如何取?
//正常的做法应该是将苹果一个一个拿出来,直到n个苹果被取出来。

//又假设有1000个苹果和10只箱子,如何快速的取出n个苹果呢?
//可以在每个箱子中放 2^i (i<=0<=n)个苹果,也就是 1、2、4、8、16、32、64、128、256、489(最后的余数)
//相当于把十进制的数用二进制来表示,取任意n个苹果时,只要推出几只箱子就可以了。

//二进制优化的思路,就是将M个相同物品,堆分成1、2、4、8、...这“几个”大小不等的物品
//通过对这几个堆的取舍,来完成达到拿走n个物品的的最优情况

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值