首页 > 代码库 > 硬币问题 (dp,多重背包的二分优化)

硬币问题 (dp,多重背包的二分优化)

题目描述

给你n种硬币,知道每种的面值Ai和每种的数量Ci。问能凑出多少种不大于m的面值。

输入

有多组数据,每一组第一行有两个整数 n(1≤n≤100)和m(m≤100000),第二行有2n个整数,即面值A1,A2,A3,…,An和数量C1,C2,C3,…,Cn (1≤Ai≤100000,1≤Ci≤1000)。所有数据结束以2个0表示。

输出

每组数据输出一行答案。

样例输入

3 101 2 4 2 1 12 51 4 2 10 0

样例输出

84

背包问题的复杂度是n*m,这题如果直接按照多重背包的计算会超时。所以和上一篇一样,多重背包的二分优化。

 详细的说明在  http://www.cnblogs.com/agenthtb/p/5792628.html

代码如下:
 1 #include <bits/stdc++.h> 2  3 using namespace std; 4  5 int w[100010],v[20010],maxn=0,dp[100010],n,m,cnt,x; 6 void f(int weight,int num)//把硬币进行二分的合并 7 { 8     for (int i=1;;i*=2) 9     {10         if (num>=i)11         w[cnt++]=weight*i;12         else13         {14             w[cnt++]=num*weight;15             return;16         }17         num-=i;18         if (num<=0)19         return;20     }21 }22 int main()23 {24     //freopen("de.txt","r",stdin);25     while (~scanf("%d%d",&n,&m))26     {27         if (n==0&&m==0)28         break;29         memset(w,0,sizeof w);30         memset(v,0,sizeof v);31         memset(dp,0,sizeof dp);32         dp[0]=1;33         set<int>s;34         cnt=1;35         for (int i=1;i<=n;++i)36         scanf("%d",&v[i]);37         for (int i=1;i<=n;++i)38         {39             scanf("%d",&x);40             f(v[i],x);41         }42         for (int i=1;i<cnt;++i)43         {44             for (int j=m;j>=w[i];--j)45             {46                 if (dp[j-w[i]])47                 dp[j]=1;48             }49         }50         int ans=0;51         for (int i=1;i<=m;++i)52         if (dp[i]) ans++;53         printf("%d\n",ans);54     }55     return 0;56 }

 

硬币问题 (dp,多重背包的二分优化)