• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

plank george

人生豪迈,只不过是重头再来。
  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

母函数解题和拓展母函数

原题:给定硬币1 5 10 25 50,数目不限,问构成总数n有多少种方法?

很简单,这是一个普通的母函数类型,通解。代码如下:

 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 #define mem0(f) memset(f,0,sizeof(f))
 5 #define M 300
 6 int c1[M],c2[M];
 7 int main()
 8 {
 9     int n;
10     int m[5]={1,5,10,25,50};
11     while(cin>>n)
12     {
13         if(n)cout<<1<<endl;
14         else
15         {
16             mem0(c1);
17             mem0(c2);
18             for(int i=0;i<=n;i++)
19                 c1[i]=1;
20             for(int i=1;i<5;i++)//表示括号
21             {
22                 for(int k=0;k<=n;k++)//大括号的数据
23                 {
24                 for(int j=0;j+k<=n;j+=m[i])
25                         {
26                             c2[j+k]+=c1[k];
27                         }
28                 }
29                 for(int j=0;j<=n;j++)
30                 {
31                     c1[j]=c2[j];
32                 }
33                 mem0(c2);
34             }
35             cout<<c1[n]<<endl;
36         }
37     }
38     return 0;
39 }
View Code

变体:

题意,硬币1 5 10 25 50,在使用的总硬币数不大于100的情况下构成总数n有多少种方法?

解析:这个题目数据特殊,可以打表做,dp也是通法,这里只介绍母函数法。

母函数的一般思路,一个c1[i]数组,表示构成总数i有c1[i]种方法,另一个c2数组作为中间存储数组,核心思路是一个个括号展开,

在这里为了表示钱币数,数组拓展表示为c1[i][k]表示用k个硬币构成总数i有c1[i][k]种方法。代码如下

 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 #define mem0(f) memset(f,0,sizeof(f))
 5 #define M 300
 6 int c1[M],c2[M];
 7 int ctz1[M][M],ctz2[M][M];//ctz[i][k]表示用k个银币组成总数i的可能方法书为ctz[i][k]
 8 int main()
 9 {
10     int n;
11     int m[5]={1,5,10,25,50};
12     while(cin>>n)
13     {
14         if(!n)cout<<1<<endl;
15         else
16         {
17             mem0(ctz1);
18             mem0(ctz2);
19             mem0(c1);
20             mem0(c2);
21            //for(int i=0;i<=n;i++)
22                 //c1[i]=1;
23             //ctz1[0][0]=1;
24             for(int i=0;i<=n&&i<=100;i++)
25             {
26                 ctz1[i][i]=1;
27             }
28             for(int i=1;i<5;i++)//表示括号
29             {
30                 for(int k=0;k<=n;k++)//大括号的数据
31                 {
32                 for(int j=0;j*m[i]+k<=n;j++)
33                         {
34                             //c2[j+k]+=c1[k];
35                             for(int p=0;p+j<=100;p++)
36                             {
37                                 ctz2[j*m[i]+k][p+j]+=ctz1[k][p];
38                             }
39                         }
40                 }
41                 for(int j=0;j<=n;j++)
42                 {
43                     for(int p=0;p<=100;p++)
44                     {
45                         ctz1[j][p]=ctz2[j][p];
46                     }
47                     //c1[j]=c2[j];
48                 }
49                // mem0(c2);
50                mem0(ctz2);
51             }
52             int s=0;
53             for(int i=0;i<=100;i++)
54             {
55                 s+=ctz1[n][i];
56             }
57             cout<<s<<endl;
58             //cout<<c1[n]<<endl;
59         }
60     }
61     return 0;
62 }
View Code

熟练之后,代码就可以精简了:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 using namespace std;
 5 #define M 300
 6 #define mem0(f) memset(f,0,sizeof(f))
 7 int coin[5]={1,5,10,25,50};
 8 int c1[M][M],c2[M][M];//c1[i][k]表示用k个硬币组成总数i共有c1[i][k]
 9 int s;
10 int main()
11 {
12     int n;
13     while(~scanf("%d",&n))
14     {
15         mem0(c1);
16         mem0(c2);
17         s=0;
18         //初始化,不要忘记了
19         for(int i=0;i<=100;i++)
20             c1[i][i]=1;
21         for(int i=1;i<=4;i++)//还有四个括号要合并
22         {
23             for(int k=0;k<=n;k++)//已经展开了的式子
24             for(int p=0;p*coin[i]+k<=n;p++)
25                 {
26                     for(int j=0;j+p<=100;j++)
27                         {
28                               c2[p*coin[i]+k][j+p]+=c1[k][j];
29                         }
30                 }
31             for(int i=0;i<=n;i++)
32                 {
33                     for(int k=0;k<=100;k++)
34                         c1[i][k]=c2[i][k];
35                 }
36             mem0(c2);
37         }
38         for(int i=0;i<=100;i++)
39         {
40             s+=c1[n][i];
41         }
42         printf("%d\n",s);
43     }
44     return 0;
45 }
View Code

 

这样母函数的使用范围又多了一些。

以后就是教我们找拼钱的最小数也不怕了

依然母函数法,找ctz[n][i]for i=0开始找ctz[n][i]非0的i。

通解万岁!

posted on 2013-07-21 11:27  plank george  阅读(165)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3