母函数入门

一篇写的很不错的博客链接:https://blog.csdn.net/xiaofei_it/article/details/17042651

母函数通常解决类似如下的问题:

给5张1元,4张2元,3张5元,要得到15元,有多少种组合?

某些时候会规定至少使用3张1元、1张2元、0张5元。

某些时候会规定有无数张1元、2元、5元。

……

其实母函数的问题大部分也可以用dp/背包来写,但是我太弱了 感觉dp容易写错 有个母函数这样比较容易理解的模板更好。

接下来是几道我母函数入门的题目:

我这里都运用了last标记来优化母函数的效率,这样效率更高。

HDU1085

题意:面值分别为1,2,5的硬币,给出它们的数量。问不能组成的最小金额是多少。

思路:母函数跑出金额为n的组合方案数有多少种,从1开始遍历,第一个方案数为 0 的n就是答案。

代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<algorithm>
 4 #define mem(a, b) memset(a, b, sizeof(a))
 5 using namespace std;
 6 
 7 int val[5], num[5], last, last2;
 8 int temp[8100], ans[8100];
 9 
10 int main()
11 {
12     val[0] = 1, val[1] = 2, val[2] = 5;
13     while(scanf("%d%d%d", &num[0], &num[1], &num[2]) != EOF)
14     {
15         if(num[0] + num[1] + num[2] == 0)
16             break;
17         mem(ans, 0), mem(temp, 0);
18         last = 0, ans[0] = 1;
19         for(int i = 0; i < 3; i ++)
20         {
21             last2 = min(last + val[i] * num[i], 8100);
22             memset(temp, 0, sizeof(int) * (last2 + 1));
23             for(int j = 0; j <= num[i] && j * val[i] <= last2; j ++)
24             {
25                 for(int k = 0; k <= last && k + j * val[i] <= last2; k ++)
26                     temp[k + j * val[i]] += ans[k];
27             }
28             memcpy(ans, temp, sizeof(int) * (last2 + 1));
29             last = last2;
30         }
31         for(int i = 0; i <= 8010; i ++)
32         {
33             if(ans[i] == 0)
34             {
35                 printf("%d\n", i);
36                 break;
37             }
38         }
39     }
40     return 0;
41 }
HDU1085

HDU2079

题意:有k行,每行有两个整数a(1 <= a <= 8),b(1 <= b <= 10),表示学分为a的课有b门。输出一个整数,表示学n个学分的组合数。

代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<algorithm>
 4 #define mem(a, b) memset(a, b, sizeof(a))
 5 using namespace std;
 6 
 7 int n, k;
 8 int val[10], num[10];
 9 int last, last2, temp[50], ans[50];
10 
11 int main()
12 {
13     int T;
14     scanf("%d", &T);
15     while(T --)
16     {
17         last = 0;
18         mem(temp, 0), mem(ans, 0);
19         ans[0] = 1;
20         scanf("%d%d", &n, &k);
21         for(int i = 0; i < k; i ++)
22             scanf("%d%d", &val[i], &num[i]);
23         for(int i = 0; i < k; i ++)
24         {
25             last2 = min(last + val[i] * num[i], n);
26             memset(temp, 0, sizeof(int) * (last2 + 1));
27             for(int j = 0; j <= num[i] && j * val[i] <= last2; j ++)
28             {
29                 for(int k = 0; k <= last && k + j * val[i] <= last2; k ++)
30                 {
31                     temp[k + j * val[i]] += ans[k];
32                 }
33             }
34             memcpy(ans, temp, sizeof(int) * (last2 + 1));
35             last = last2;
36         }
37         printf("%d\n", ans[n]);
38     }
39     return 0;
40 }
HDU2079

HDU2152

题意:n种水果,求出m个水果的组合方案数。但是这里有数量限制,有下限跟上限

代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<algorithm>
 4 #define mem(a, b) memset(a, b, sizeof(a))
 5 using namespace std;
 6 
 7 int n, m, down[110], up[110];
 8 int last, last2;
 9 int ans[110], temp[110];
10 
11 int main()
12 {
13     while(scanf("%d%d", &n, &m) != EOF)
14     {
15         for(int i = 0; i < n; i ++)
16             scanf("%d%d", &down[i], &up[i]);
17         mem(temp, 0), mem(ans, 0);
18         ans[0] = 1, last = 0;
19         for(int i = 0; i < n; i ++)
20         {
21             last2 = min(last + up[i], m);
22             memset(temp, 0, sizeof(int) * (last2 + 1));
23             for(int j = down[i]; j <= up[i] && j <= last2; j ++)
24             {
25                 for(int k = 0; k <= last && k + j <= last2; k ++)
26                 {
27                     temp[k + j] += ans[k];
28                 }
29             }
30             memcpy(ans, temp, sizeof(int) * (last2 + 1));
31             last = last2;
32         }
33         printf("%d\n", ans[m]);
34     }
35     return 0;
36 }
HDU2152

模板:

 1 ans[0] = 1, last = 0;  //初始化 多组输入的话还要 mem(ans, 0), mem(temp, 0) 
 2 for(int i = 0; i < k; i ++)
 3 {
 4     last2 = min(last + val[i] * num[i], n);//计算下一次的last 
 5     memset(temp, 0, sizeof(int) * (last2 + 1));//只清空temp[0...last2] 
 6     for(int j = down[i]; j <= up[i] && j * val[i] <= last2; j ++)//遍历因子中的每一项 注意数量限制 上下限 
 7     {
 8         for(int k = 0; k <= last && k + j * val[i] <= last2; k ++)//遍历答案数组,更新答案,因子乘之前因子相乘得到的临时答案 
 9         {//ans里是上一次的last,temp里是last2 
10             temp[k + j * val[i]] += ans[k];
11         }
12     }
13     memcpy(ans, temp, sizeof(int) * (last2 + 1));//temp赋值给ans 作为临时答案 待更新 
14     last = last2;//更新last 
15 }

 

posted @ 2019-07-23 19:04  缘未到  阅读(155)  评论(0编辑  收藏  举报