ZOJ1366 POJ1276 Cash Machine 经典多重背包问题

105 篇文章 0 订阅
48 篇文章 0 订阅

这是一条非常经典的题,是0/1背包问题的变种,详细可以看看《背包9讲-多重背包问题》。简单说一下,对于n1 D1 n2 D2 ... nN DN,n为D的数量,则可以把n分解为k1=1,k2=2,k3=4,k4=8...km,且k1+k2+...km=n,其中k1,k2...km-1为2的幂,km不一定是2的幂。这个要表达清楚是很困难的,你们可以看一下《背包9讲》。同时背包问题有两种不同的问法,即恰好装满背包和不要求装满背包,大家可以看一下以下这段解释。

以下摘自《背包9讲》

我们看到的求最优解的背包问题题目中,事实上有两种不太相同的问法。有的题目要求“恰好装满背包”时的最优解,有的题目则并没有要求必须把背包装满。一种区别这两种问法的实现方法是在初始化的时候有所不同。

如果是第一种问法,要求恰好装满背包,那么在初始化时除了f[0]为0其它f[1..V]均设为-∞,这样就可以保证最终得到的f[N]是一种恰好装满背包的最优解。

如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将f[0..V]全部设为0。

为什么呢?可以这样理解:初始化的f数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为0的背包可能被价值为0的nothing“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是-∞了。如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。

这个小技巧完全可以推广到其它类型的背包问题,后面也就不再对进行状态转移之前的初始化进行讲解。


代码如下


/*******************************************************************************
 * Author : Neo Fung
 * Email : neosfung@gmail.com
 * Last modified : 2011-07-18 17:41
 * Filename : ZOJ1366 POJ1276 Cash Machine.cpp
 * Description :	http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1366
					http://poj.org/problem?id=1276
 * *****************************************************************************/
// ZOJ1366 POJ1276 Cash Machine.cpp : Defines the entry point for the console application.
//

// #include "stdafx.h"



#include <fstream>
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <string>
#include <vector>
#include <stack>
#include <deque>
#include <map>
#include <math.h>
#include <algorithm>
#include <numeric>
#include <functional>
#include <memory.h>

using namespace std;

int main(void)
{
// 	ifstream cin("data.txt");
	int cash,N;
	int n,D;
	int temp;
	vector<int> cashVec;
	int *DP=new int[100001];
	int power[]={1,2,4,8,16,32,64,128,256,512};


	while(cin>>cash>>N)
	{

		for (int i=0;i<=cash;++i)
		{
			DP[i]=0;
		}
		DP[0]=0;
		cashVec.clear();
		cashVec.push_back(0);

		for(int i=1;i<=N;++i)
		{
			cin>>n>>D;
			if(!n) 
				temp=0;
			else
				temp = log((n+1)*1.0) / log(2.0) + 0.99999999;


			for(int j=0;j<temp;++j)
			{
				if(n>power[j])
				{
					cashVec.push_back(D*power[j]);
					n -=power[j];
				}
				else
				{
					cashVec.push_back(n*D);
				}
			}
		}

		if(cash == 0 || N==0)
		{
			cout<<0<<endl;
			continue;
		}

		for(int i=1;i<cashVec.size();++i)
			if (cash < cashVec.at(i))
			{
				continue;
			}
			else
			{
				for(int j=cash;j>= cashVec.at(i);--j)
				{
					// 				if(j<=cashVec.at(i))
					DP[j] = max(DP[j],DP[j-cashVec.at(i)]+cashVec.at(i));
				}
			}


		cout<<DP[cash]<<endl;
	}

	cashVec.clear();
	delete []DP;

	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值