Coins [POJ1742] [DP]

 
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-1][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\n",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 }
View Code

猜你喜欢

转载自www.cnblogs.com/ibilllee/p/9211093.html