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;
}