多重背包之七

本文深入探讨了多重背包和完全背包问题的解决方法,包括如何通过二进制方法将多重背包问题转化为01背包问题,以及如何利用完全背包问题解决售货员找零的最优化问题。通过上界分析证明了最优解的存在性,并提供了具体的实现步骤。
//poj 3260 The Fewest Coins

/*

题意:John带了n种币值Vi的确定数量Ci的硬币,而shopkeeper的硬币无限多.
给出T,求John支付的硬币数目加上售货员找零的硬币数目的最小值。如果无法支付T,输出-1
支付时硬币数量有限制,为多重背包问题. 找零时硬币数量无限制,为完全背包问题
*/


#include
<iostream> //多重背包和完全背包
using namespace std;
int main()
{
int n,t,euro[110],num[110],dp[30000],maxn;
cin
>>n>>t;
int mx=0;
for(int i=1;i<=n;++i)
{
cin
>>euro[i];
mx
=max(mx,euro[i]);
}
maxn
=mx*mx+t; //上界
for(int i=1;i<=n;++i)
cin
>>num[i];
fill(dp,dp
+maxn+1,-1);
dp[
0]=0;

//John付钱 多重背包,通过二进制方法转化为01背包
for(int i=1;i<=n;++i)
{
int k=1,s=num[i];
while(s>=k)
{
for(int j=maxn;j>=euro[i]*k;--j)
if(dp[j-euro[i]*k]!=-1)
{
if(dp[j]==-1)
dp[j]
=dp[j-euro[i]*k]+k; //注意是 +k
else
dp[j]
=min(dp[j],dp[j-euro[i]*k]+k);
}
s
-=k;k*=2;
}
for(int j=maxn;j>=euro[i]*s;--j)
if(dp[j-euro[i]*s]!=-1)
{
if(dp[j]==-1)
dp[j]
=dp[j-euro[i]*s]+s;
else
dp[j]
=min(dp[j],dp[j-euro[i]*s]+s);
}
}

//shopkeeper找钱 完全背包
for(int i=1;i<=n;++i)
{
for(int j=maxn-euro[i];j>0;--j) //因为是减,所以要逆序循环
if(dp[j+euro[i]]!=-1)
{
if(dp[j]==-1)
dp[j]
=dp[j+euro[i]]+1;
else
dp[j]
=min(dp[j],dp[j+euro[i]]+1);
}
}

cout
<<dp[t]<<endl;
return 0;
}

/*

上界为:T+maxValue^2,其中maxValue为最大硬币面值。
证明:反证法。假设存在一种支付方案,John给的钱超过T+maxValue^2, 则售货员找零超过maxValue^2,找的硬币数目超过maxValue个,将其看作一数列,求前n项和sum(n),
根据鸽巢原理,至少有两 个对maxValue求模的值相等,假设为sum(i)和sum(j),i<j,则i+1...j的硬币面值和为maxValue的倍数,
同理,John给的钱中也有 一定数量的硬币面值和为maxValue的倍数,
则这两堆硬币可用数量更少的maxValue面值硬币代替,产生更优方案。

*/

  

转载于:https://www.cnblogs.com/mjc467621163/archive/2011/08/22/2149186.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值