背包问题的二进制优化详解

背包问题的二进制优化详解

以POJ1276为例,说一下二进制优化的详细思路,首先贴题
Description
A Bank plans to install a machine for cash withdrawal. The machine is able to deliver appropriate @ bills for a requested cash amount. The machine uses exactly N distinct bill denominations, say Dk, k=1,N, and for each denomination Dk the machine has a supply of nk bills. For example,
N=3, n1=10, D1=100, n2=4, D2=50, n3=5, D3=10
means the machine has a supply of 10 bills of @100 each, 4 bills of @50 each, and 5 bills of @10 each.
Call cash the requested amount of cash the machine should deliver and write a program that computes the maximum amount of cash less than or equal to cash that can be effectively delivered according to the available bill supply of the machine.
Notes:
@ is the symbol of the currency delivered by the machine. For instance, @ may stand for dollar, euro, pound etc.
Input
The program input is from standard input. Each data set in the input stands for a particular transaction and has the format:
cash N n1 D1 n2 D2 … nN DN
where 0 <= cash <= 100000 is the amount of cash requested, 0 <=N <= 10 is the number of bill denominations and 0 <= nk <= 1000 is the number of available bills for the Dk denomination, 1 <= Dk <= 1000, k=1,N. White spaces can occur freely between the numbers in the input. The input data are correct.
Output
For each set of data the program prints the result to the standard output on a separate line as shown in the examples below.
Sample Input
735 3 4 125 6 5 3 350
633 4 500 30 6 100 1 5 0 1
735 0
0 3 10 100 10 50 10 10
Sample Output
735
630
0
0
题目大意:给定背包的容量V,和n中物品,每种物品的数量、重量、价值分别为num,w,v。求背包可以获得的最大价值。
假如按照一般的解决多重背包的思维,转换为0,1背包后时间复杂度大约为O(cash * N * Dk),在多数类似题目下会超时,下面来看一下二进制优化的方案:
以input第一组数735 3 4 125 6 5 3 350为例,有4件价值为125的物品,6件价值为5的物品,同时还有3件价值为350的物品,我们可以这样拆分:
1件价值为125的物品,1件价值为250的物品,1件价值为125的物品;1件价值为5的物品,1件价值为10的物品,一件价值为15的物品;1件价值为350的物品,一件价值为700的物品
我们可以通过1,2,4,8四个数字任意组合得到1-15内的所有数字。同样的,当Dk特别大的时候,这里最大为1000,我们就以1000为例,可以将1000拆分为1,2,4,8,16,32,64,128,256和489,那么1000件物品就被我们缩小变成了10件物品,时间复杂度也大大降低(1000以内的任何一个数字都能用这10个数字最多取一次相加得到),这样的话时间复杂度撑死就O(cash * 10 *10),接下来再用一维数组求解就容易很多了,最后贴个随手写的代码

#include <iostream>
#include <cmath>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
int buy[1000002];
int bill[10002];
int main(){
	int cash,N;//0-100000,0-10
	while(~scanf("%d%d",&cash,&N)){
		int ni,di;//0-1000,1-1000
		int n = 1;
		memset(bill,0,sizeof(bill));
		for(int j=0;j<N;j++){
			cin>>ni>>di;
			int k = 0;
			while(ni-(1<<(k+1))+1>0)k++;
			for(int i=0;i<k;i++){
				bill[n++] = (1<<i)*di;
			}
			bill[n++] = (ni - (1<<k) + 1)*di;
		}
		memset(buy,0,sizeof(buy));
		for(int i=1;i<n;i++){
			for(int j=cash;j>=bill[i];j--){
				buy[j] = max(buy[j],bill[i]+buy[j-bill[i]]);
			}
		}
		cout<<buy[cash]<<endl;
	} 
	return 0;
} 
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值