https://loj.ac/problem/10179
题目描述
有(n)中面值的硬币,每种硬币有一定的数量,求凑出面值(k)最少要多少枚硬币。
思路
首先比较显然的是我们可以写出一个(O(n·k·c_{max})),我们考虑暴力枚举每(i)种硬币的个数,第一维枚举钱数即可。这样理论实践复杂度肯定过不去,我们需要进行优化。考虑一下对于当前枚举的钱数(v),显然如果(k)时可取的,(k+v)也是可取的,显然会用其中代价较小的进行更新,所以我们可以第二维暴力枚举余数,对于当前余数,如果要更新(i),显然是由(i-b[i]*c[i]sim i)同余数的最小的那个,为了使得每一个数都在“同一起跑线”上,我们需要先减去两两之间差的代价。
代码
#include<bits/stdc++.h>
using namespace std;
int read()
{
int res=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){res=(res<<3)+(res<<1)+(ch^48);ch=getchar();}
return res*w;
}
int b[220],c[220],q[20020],pos[20020],f[20020];
int main()
{
int n=read();
for(int i=1;i<=n;i++)
b[i]=read();
for(int i=1;i<=n;i++)
c[i]=read();
int k=read();
memset(f,0x3f,sizeof(f));
f[0]=0;
for(int i=1;i<=n;i++){//cout<<i<<':'<<endl;
for(int j=0;j<b[i];j++)
{
int head=0,tail=0;
q[head]=0x3f3f3f3f;
for(int p=j;p<=k;p+=b[i])
{
int v=f[p]-p/b[i];
while(head<=tail&&pos[head]<p-c[i]*b[i])head++;
while(head<=tail&&q[tail]>=v)tail--;
q[++tail]=v;pos[tail]=p;
f[p]=min(f[p],q[head]+p/b[i]);
// cout<<p<<' '<<f[p]<<endl;
}
}
}
printf("%d
",f[k]);
}