有的物品只可以取一次或不取(基本的0-1背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包),就是混合背包问题。
The Fewest Coins
题意:
农夫John到城里去买一些农具。John是一个非常有效率的人,他总是以这样一种方式来买他的货物:最少数量的硬币易手,也就是他用来支付的硬币的数量加上他收到的找零的硬币数量要最小化。请您帮助他确定这个最小值是多少。
农夫John想购买T (1≤T≤10,000)美分的商品。货币体系有N (1 ≤ N ≤ 100)种不同的硬币,其面值为V1, V2, …, VN (1 ≤ Vi ≤ 120)。农夫John有面值为V1的硬币 C1枚,面值为 V2的硬币C2枚,…,面价为VN的硬币CN 枚(0 ≤ Ci ≤ 10,000)。店主则拥有所有的硬币无限枚,并且总是以最有效的方式进行找零(尽管农夫John必须确保以能够进行正确的找零方式付款)。
输入
第一行:两个用空格分隔的整数N和T;
第二行:N个用空格分隔的整数,分别为V1, V2, …, VN;
第三行:N个用空格分隔的整数,分别为 C1, C2, …, CN。
输出
输出1行,给出一个整数,在支付和找零中使用硬币的最小数。如果农夫John支付和接收准确的找零是不可能的,输出-1。
题解
首先用完全背包预处理找零j的时候最少需要多少硬币,然后用多重背包处理付款j的时候最少需要多少银币,最后将两个加起来就行,求最小值。
#include <cstdio>
#include <cmath>
#include <cstring>
#include <iostream>
using namespace std;
const int inf=0x3f3f3f3f;
//约翰对不同金额所付的最少硬币数量
int dp1[25000];
//店长对不同金额所付的最少硬币数量
int dp2[25000];
int v[105],w[105];
//完全背包
void com(int w,int sum)
{
for(int i=w;i<=sum;i++)
dp2[i]=min(dp2[i],dp2[i-w]+1);
}
//多重背包
void mul(int v,int w,int sum)
{
if(w*v>=sum){
for(int i=w;i<=sum;i++)
dp1[i]=min(dp1[i],dp1[i-w]+1);
}else{
int k=1;
while(k<v)
{
for(int i=sum;i>=k*w;i--)
dp1[i]=min(dp1[i],dp1[i-k*w]+k);
v-=k;
k*=2;
}
for(int i=sum;i>=v*w;i--)
dp1[i]=min(dp1[i],dp1[i-v*w]+v);
}
}
int main()
{
int n,t;
scanf("%d%d",&n,&t);
int sum=0;
for(int i=0;i<n;i++)
{
scanf("%d",&w[i]);
sum=max(sum,w[i]);
}
for(int i=0;i<n;i++)
scanf("%d",&v[i]);
sum=sum*sum+t+1;
memset(dp1,inf,sizeof(dp1));
memset(dp2,inf,sizeof(dp2));
dp1[0]=0;dp2[0]=0;
for(int i=0;i<n;i++)
{
com(w[i],sum);
mul(v[i],w[i],sum);
}
int res=inf;
for(int i=t;i<=sum;i++)
res=min(res,dp1[i]+dp2[i-t]);
if(res==inf)
cout<<-1<<endl;
else
cout<<res<<endl;
return 0;
}