P2851 [USACO06DEC] 最少的硬币 G
题解:从题目上看到那个有n种不同的货币,对于买家来说每个货币有C[ i ]个,是有限个数的,但是对于卖家来说 每个货币都是无限的,题目中要我们求的是买到这个物品的最小交易的货币数,交易的货币数=买家付钱货币数+卖家找钱货币数,我们的dp数组的含义肯定是金额为 j 的最小交易货币数,但是有可能掏的钱多了,还能有更小的货币数,所以到时候在最后判断的时候范围要在m~m+mx^2(mx是货币的最大金额)
然后我们对卖家进行完全背包,对买家进行多重背包
#include<bits/stdc++.h>
using namespace std;
#define int long long
int MAXN=10005+(120*120);
int t;
int n;
int c[150];//w[i][0]存储的是货币数量,w[i][1]是总价值
int v[105];
int dp1[10005+(120*120)];//价值为j的物品所需的最小硬币为dp[j]
int dp2[10005+(120*120)];
int sum;
int mx;//所有货币中最大的金额
signed main()
{
memset(dp1,0x3f3f3f3f,sizeof(dp1));
memset(dp2,0x3f3f3f3f,sizeof(dp2));
dp1[0]=0,dp2[0]=0;
cin>>n>>t;
for(int i=1;i<=n;i++)
{
cin>>v[i];
if(mx<v[i])
mx=v[i];
}
int p=0;
int cnt=0;
for(int i=1;i<=n;i++)
{
cin>>c[i];
sum+=c[i]*v[i];
}
if(sum<t)
{
cout<<"-1"<<"\n";
return 0;
}
//先算找回的,完全背包
for(int i=1;i<=n;i++)
{
for(int j=v[i];j<=mx*mx;j++)
{
dp2[j]=min(dp2[j],dp2[j-v[i]]+1);
}
}
//购买物品时多重背包
for(int i=1;i<=n;i++)
{
//二进制优化
for (int j=1;j<=c[i];j<<=1)
{
for (int k=t+mx*mx;k>=j*v[i];k--)
{
dp1[k]=min(dp1[k], dp1[k-j*v[i]]+j);
}
c[i]-=j;
}
if (c[i])
{
for (int k=t+mx*mx;k>=c[i]*v[i];k--)
{
dp1[k]=min(dp1[k], dp1[k-c[i]*v[i]]+c[i]);
}
}
}
int ans=0x3f3f3f3f;
for (int i=t;i<=t+mx*mx;i++)
ans=min(ans,dp1[i]+dp2[i-t]);
printf("%d\n",ans==0x3f3f3f3f?-1:ans);
return 0;
}