题解
一个自然的思路是对于每一个物品做一次01背包
然后T飞了。
试着用二进制拆分,还是T了。
单调队列,对不起,懒,不想写。
我们这样想。设dp[i]代表i这个面值前几种硬币是否能凑到
然后对于每一个i,我们用used[i]代表要凑到i这个值至少要多少个当前这种硬币
然后used可以o(m)得到(当dp[i]=1时,used[i]=0,否则dp[i]=used[dp[i-a]]+1),对于一个used[i]<=c我们把dp[i]变为1.
完成了转移这样复杂度为O(n*m)
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 const int N=110; 8 const int M=100100; 9 int n,m,dp[M],a[N],c[N],ans,used[M];; 10 int main(){ 11 while(scanf("%d%d",&n,&m)!=EOF){ 12 if(n==0&&m==0)break; 13 memset(dp,0,sizeof(dp)); 14 dp[0]=1; 15 for(int i=1;i<=n;i++){scanf("%d",&a[i]);} 16 for(int i=1;i<=n;i++){scanf("%d",&c[i]);} 17 for(int i=1;i<=n;i++){ 18 for(int j=0;j<=m;j++)used[j]=0; 19 for(int j=a[i];j<=m;j++){ 20 if(!dp[j]&&dp[j-a[i]]&&used[j-a[i]]<c[i]){ 21 dp[j]=1;used[j]=used[j-a[i]]+1; 22 } 23 } 24 } 25 ans=0; 26 for(int i=1;i<=m;i++)ans+=dp[i]; 27 printf("%d ",ans); 28 } 29 return 0; 30 }