POJ3260 Solution
题解
混合背包呐,约翰所付的钱为多重背包,商店的找零为完全背包。而最大容量,也就是约翰的最大花费为\(T+maxn^2\)。
证明:若约翰花费\(>T+maxn^2\),则找零的硬币数\(>maxn\),而约翰除必须付的\(T\)元钱外支付的硬币数也\(\ge maxn\)。将这两类硬币分别排序,可以得到\(\ge maxn\)种前缀和。根据抽屉原理,两者都一定存在一个子序列使其之和可以被\(maxn\)整除。将该子序列全部换为面值为\(maxn\)的硬币,重复的部分可以两个人都不花费,与硬币数最少的条件矛盾。
分别dp后枚举约翰所付钱数即可。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int T=10000,N=100,inf=0x3f3f3f3f;
int w[10*N],v[N],c[N],v2[10*N],dp1[3*T],dp2[3*T];
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0' || ch>'9') {if(ch=='-') w=-1; ch=getchar();}
while(ch>='0' && ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
int main()
{
int n=read(),t=read(),ma=0,cnt=0;
for(int i=1;i<=n;i++) v[i]=read(),ma=max(ma,v[i]);
for(int i=1;i<=n;i++) c[i]=read();
int x,qwq=t+ma*ma,ans=inf;
for(int i=1;i<=n;i++)
{
for(int j=1;c[i]>0;j<<=1)
{
x=min(c[i],j),c[i]-=x;
v2[++cnt]=v[i]*x,w[cnt]=x;
}
}
memset(dp1,0x3f,sizeof(dp1)),dp1[0]=0;
memset(dp2,0x3f,sizeof(dp2)),dp2[0]=0;
for(int i=1;i<=cnt;i++)
for(int j=qwq;j>=v2[i];j--) dp1[j]=min(dp1[j],dp1[j-v2[i]]+w[i]);
for(int i=1;i<=n;i++)
for(int j=v[i];j<=qwq;j++) dp2[j]=min(dp2[j],dp2[j-v[i]]+1);
for(int i=t;i<=qwq;i++) ans=min(ans,dp1[i]+dp2[i-t]);
if(ans>=inf) printf("-1");
else printf("%d",ans);
return 0;
}