Description
给出硬币面额及每种硬币的个数,求从1到m能凑出面额的个数。
Input
多组数据,每组数据前两个数字为n,m。n表示硬币种类数,m为最大面额,之后前n个数为每种硬币的面额,后n个数为相应每种硬币的个数。
(n<=100,m<=100000,面额<=100000,每种个数<=1000)
Output
如题
Sample Input
3 10
1 2 4 2 1 1
2 5
1 4 2 1
0 0
Sample Output
8
4
Solution
这是典型的多重部分和问题
首先我们很容易想到的就是记录dp[i][j]表示用前i中数字能否加成j
那么状态转移方程就是dp[i][j]|=dp[i-1][j-k*a[i]]
我们可以发现时间复杂度是O(m*∑面额),这并不优秀!
因为dp时求bool值一般不会很高效,我们换种思路
记dp[i][j]为,用前i个数加成j,i种最多剩下多少个
那我们就有以下方程:
if(dp[i-1][j]>=0) dp[i][j]=num[i];
else if(dp[i-1][j-val[i]]<=0||val[i]>j) dp[i][j]=-1;
else dp[i][j]=dp[i][j-val[i]]-1;
最后统计dp[n][i]>=0的个数
Code

1 #include<set> 2 #include<map> 3 #include<queue> 4 #include<stack> 5 #include<cmath> 6 #include<cstdio> 7 #include<cstring> 8 #include<iostream> 9 #include<algorithm> 10 #define RG register int 11 #define rep(i,a,b) for(RG i=a;i<=b;++i) 12 #define per(i,a,b) for(RG i=a;i>=b;--i) 13 #define ll long long 14 #define inf (1<<29) 15 #define maxn 105 16 #define maxm 100005 17 using namespace std; 18 int n,m; 19 int val[maxn],num[maxn]; 20 int dp[maxm]; 21 inline int read() 22 { 23 int x=0,f=1;char c=getchar(); 24 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 25 while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} 26 return x*f; 27 } 28 29 void DP() 30 { 31 memset(dp,-1,sizeof(dp)); 32 dp[0]=0; 33 rep(i,1,n) 34 rep(j,0,m) 35 { 36 if(dp[j]>=0) dp[j]=num[i]; 37 else if(val[i]>j||dp[j-val[i]]<=0) dp[j]=-1; 38 else dp[j]=dp[j-val[i]]-1; 39 } 40 int ans=0; 41 rep(i,1,m) 42 if(dp[i]>=0) ans++; 43 printf("%d ",ans); 44 } 45 46 int main() 47 { 48 while(1) 49 { 50 n=read(),m=read(); 51 if(!n&&!m) return 0; 52 rep(i,1,n) val[i]=read(); 53 rep(i,1,n) num[i]=read(); 54 DP(); 55 } 56 return 0; 57 }