这是一条非常经典的题,是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;
}