HDU 1284(钱币兑换 背包/母函数)

HDU 1028 相似的题目。

方法一:完全背包。

限制条件:硬币总值不超过 n。 

目标:求出组合种数。

令 dp[ i ][ j ] == x 表示用前 i 种硬币组合价值为 j 的钱共 x 种方法。

状态转移方程:dp[ i ][ j ] = dp[ i - 1][ j ] + dp[ i ][ j - v[ i ] ] ;

方程解释:用前 i 种硬币组合出钱 j 的方法数 = 前 i - 1 种硬币组合出钱 j 的方法数(不用第 i 种硬币)+ 至少用一枚第 i 种硬币的方法数。

滚动数组实现,i 的这一维便可省去。

代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int maxn = 32769;
 4 int n,dp[maxn];
 5 void init()
 6 {
 7     memset(dp,0,sizeof(dp));
 8     dp[0] = 1;
 9     for(int i = 1; i <= 3; ++i)
10         for(int j = i; j < maxn; ++j)
11             dp[j] += dp[j-i];
12 }
13 int main()
14 {
15     init();
16     while(~scanf("%d",&n))
17         printf("%d\n",dp[n]);
18     return 0;
19 }
View Code

 

方法二:母函数。

用( 1 + x^k + x^(k*2) + x^(k*3) + ...) 表示参与组成的价值为 k 的硬币,1 表示硬币 k 个数为 0,每项前面的系数表示组成钱 k ( x 的次方数) 的方法数。

比如现有 1 分,3 分,6 分的硬币各 1 枚,便可得到:

( 1 + x^1 ) * ( 1 + x^3 ) * ( 1 + x^6 ) = 1 + x^1 + x^3 + x^4 + x^6 + x^7 + x^9 + x^10;

这表示用 1 分,3 分,6 分的硬币各 1 枚可以组合出钱数为 0,1,3,4,6,7,9,10 的方法各 1 种。

若是 1 分,3 分,6 分的硬币各 2 枚的话,就可得到:

( 1 + x^1 + x^2 ) * ( 1 + x^3 + x^6 ) * ( 1 + x^6 + x^12 ) =

1 + x^1 + x^2 + x^3 + x^4 + x^5 + 2*x^6 + 2*x^7 + 2*x^8 + x^9 + x^10 + x^11 + 2*x^12 + 2*x^13 + 2*x^14 + x^15 + x^16 + x^17 + x^18 + x^19 + x^20

这表示用 1 分,3 分,6 分的硬币各 2 枚可以组合出

钱数为 0 的方法 1 种,钱数为 1 的方法 1 种,钱数为 2 的方法 1 种,钱数为 3 的方法 1 种,钱数为 4 的方法 1 种,

钱数为 5 的方法 1 种,钱数为 6 的方法 2 种,钱数为 7 的方法 2 种,钱数为 8 的方法 2 种,钱数为 9 的方法 1 种,

钱数为 10 的方法 1 种,钱数为 11 的方法 1 种,钱数为 12 的方法 2 种,钱数为 13 的方法 2 种,钱数为 14 的方法 2 种,

钱数为 15 的方法 1 种,钱数为 16 的方法 1 种,钱数为 17 的方法 1 种,钱数为 18 的方法 1 种,钱数为 19 的方法 1 种,

钱数为 20 的方法 1 种。

(神奇.......要了解更多关于母函数的问题请点这里

手工模拟多项式展开,代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int maxn = 32769;
 4 int n,ans[maxn],tans[maxn];
 5 void init()
 6 {
 7     for(int i = 0; i < maxn; ++i)
 8     {
 9         ans[i] = 1;
10         tans[i] = 0;
11     }
12     for(int i = 2; i <= 3; ++i)
13     {
14         for(int j = 0; j < maxn; ++j)
15             for(int k = 0; j+k < maxn; k+=i)
16                 tans[j+k]+=ans[j];
17         for(int j = 0; j < maxn; ++j)
18         {
19             ans[j] = tans[j];
20             tans[j] = 0;
21         }
22     }
23 }
24 int main()
25 {
26     init();
27     while(~scanf("%d",&n))
28         printf("%d\n",ans[n]);
29     return 0;
30 }
View Code

 

向这些博客的作者致谢:

https://www.cnblogs.com/13224ACMer/p/4671551.html

https://blog.csdn.net/u013480600/article/details/40477769

https://blog.csdn.net/feizaoSYUACM/article/details/70040086

https://blog.csdn.net/u010304217/article/details/38374417

 

转载于:https://www.cnblogs.com/Taskr212/p/9561041.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值