母函数
1 普通型母函数
1.1 模板代码
母函数即通过幂函数的指数部分解决一些排列组合的问题。
关键在于对c1[], c2[]两个数组的理解:
数组下标储存的是指数,其数值储存的是对应的系数;
c2[]储存下次运算的系数;
c1[]储存截止当前的运算结果。
个人认为比较需要理解和分析的是三重循环中每重循环的含义。
#include <bits/stdc++.h>
using namespace std;
const int lmax = 10000;
int c1[lmax+1], c2[lmax+1];
int main()
{
int n;
while(cin>>n)
{
for(int i=0; i<=n; i++)
{
c1[i] = 0;
c2[i] = 0;
}
for(int i=0; i<=n; i++)
c1[i] = 1;
for(int i=2; i<=n; i++)
{
for(int j=0; j<=n; j++)
for(int k=0; k+j<=n; k+=i)
{
c2[j+k] += c1[j];
}
for(int j=0; j<=n; j++)
{
c1[j] = c2[j];
c2[j] = 0;
}
}
cout << c1[n] << endl;
}
return 0;
}
上述代码是以整数拆分为例:
https://acm.hdu.edu.cn/showproblem.php?pid=1028
1.2 Example
1.2.1 H Square Coins
https://acm.hdu.edu.cn/showproblem.php?pid=1398
这题就是套模板
参考代码:
#include <bits/stdc++.h>
using namespace std;
const int lmax = 10000;
int c1[lmax+1], c2[lmax+1];
int main()
{
int n;
while(cin>>n)
{
if(n==0)
break;
for(int i=0; i<=n; i++)
{
c1[i] = 0;
c2[i] = 0;
}
for(int i=0; i<=n; i++)
c1[i] = 1;
for(int i=2; i<=17; i++)
{
for(int j=0; j<=n; j++)
for(int k=0; k+j<=n; k+=i*i)
{
c2[j+k] += c1[j];
}
for(int j=0; j<=n; j++)
{
c1[j] = c2[j];
c2[j] = 0;
}
}
cout << c1[n] << endl;
}
return 0;
}
1.2.2 I Holding Bin-Laden Captive!
https://acm.hdu.edu.cn/showproblem.php?pid=1085
此题目不一样/麻烦的地方在于“n”的大小并不是确定的,好在此题目中只有3种硬币,“拆括号”时只用做2次循环,因此我选择的方法是每次循环时手动改变n值的大小,代码如下:
a = num1;
b = num2;
for(int i=2; i<=3; i++)
{
for(int j=0; j<=a; j++)
for(int k=0; k<=b*num[i-1]; k+=num[i-1])
{
c2[j+k] += c1[j];
}
for(int j=0; j<=a+b*num[i-1]; j++)
{
c1[j] = c2[j];
c2[j] = 0;
}
a = num1 + num2*num[i-1];
b = num5;
}
参考代码:
#include <bits/stdc++.h>
using namespace std;
const int lmax = 10000;
int c1[lmax+1], c2[lmax+1];
int main()
{
int num1, num2, num5, a, b;
int num[3] = {1, 2, 5};
while(scanf("%d %d %d", &num1, &num2, &num5))
{
if(num1==0&&num2==0&&num5==0)
break;
int n = 1*num1 + 2*num2 + 5*num5;
for(int i=0; i<=n; i++)
{
c1[i] = 0;
c2[i] = 0;
}
for(int i=0; i<=num1; i++)
c1[i] = 1;
a = num1;
b = num2;
for(int i=2; i<=3; i++)
{
for(int j=0; j<=a; j++)
for(int k=0; k<=b*num[i-1]; k+=num[i-1])
{
c2[j+k] += c1[j];
}
for(int j=0; j<=a+b*num[i-1]; j++)
{
c1[j] = c2[j];
c2[j] = 0;
}
a = num1 + num2*num[i-1];
b = num5;
}
int ans;
for(ans=0; ans<=n; ans++)
{
if(c1[ans]==0)
{
printf("%d\n", ans);
break;
}
}
if(ans==n+1)
printf("%d\n", ans);
}
return 0;
}
1.2.3 K The Balance
https://acm.hdu.edu.cn/showproblem.php?pid=1709
本题最大的不同在于指数上要做减法,但是数组下标不能储存负数,因此一概换种思路,先储存所有加法的情况下可以称出来的重量,再以此为基础做减法,标记减法情况下可以称出来的重量,再进一步判断加法和减法生成的两个数组就可以了,代码如下:
for(int i=sum; i>0; i--)
{
if(c1[i]!=0)
{
for(int j=1; j<i; j++)
{
if(c1[j]!=0)
b[i-j] = 1;
}
}
}
参考代码:
#include <bits/stdc++.h>
using namespace std;
const int lmax = 100010;
int c1[lmax], c2[lmax];
int main()
{
int n, qua[lmax], temp[lmax];
while(cin>>n)
{
int sum = 0, b[lmax] = {0};
for(int i=0; i<n; i++)
{
scanf("%d", &qua[i]);
sum += qua[i];
}
for(int i=0; i<=sum; i++)
{
c1[i] = 0;
c2[i] = 0;
}
for(int i=0; i<=1; i++)
c1[i*qua[0]] = 1;
for(int i=2; i<=n; i++)
{
for(int j=0; j<=sum; j++)
for(int k=0; j+k*qua[i-1]<=sum&&k<=1; k++)
{
c2[j+k*qua[i-1]] += c1[j];
}
for(int j=0; j<=sum; j++)
{
c1[j] = c2[j];
c2[j] = 0;
}
}
for(int i=sum; i>0; i--)
{
if(c1[i]!=0)
{
for(int j=1; j<i; j++)
{
if(c1[j]!=0)
b[i-j] = 1;
}
}
}
int ans = 0;
for(int i=1; i<=sum; i++)
{
if(!c1[i] && !b[i])
temp[ans++] = i;
}
cout << ans << endl;
if (ans)
{
for (int i = 0; i < ans; i++)
cout << temp[i] << " ";
cout << endl;
}
}
return 0;
}
2 指数型母函数
2.1 Example
2.1.1 M 排列组合
https://acm.hdu.edu.cn/showproblem.php?pid=1521
数学公式
指数型母函数与普通母函数的区别在于指数型母函数增加了一步初始化阶乘的步骤,其余基本不变
参考代码(模板代码):
#include <bits/stdc++.h>
using namespace std;
const int N = 21;
double c1[N], c2[N]; //注意是double类型
int val[N], F[N];
//初始化阶乘
void Factorial()
{
F[0] = 1;
for(int i=1; i<20; i++)
{
F[i] = F[i-1] * i;
}
}
int main()
{
int n, m, i, j, k;
Factorial();
while(scanf("%d %d", &n, &m)!=EOF)
{
for(int i=0; i<n; ++i)
{
scanf("%d", &val[i]);
}
memset(c1, 0, sizeof(c1));
memset(c2, 0, sizeof(c2));
for(int i=0; i<=val[0]; i++)
{
c1[i] = 1.0/F[i];
}
for(int i=1; i<n; i++)
{
for(int j=0; j<=m; j++)
{
for(int k=0; k+j<=m && k<=val[i]; k++)
{
c2[j+k] += c1[j]/F[k];
}
}
for(int j=0; j<=m; j++)
{
c1[j] = c2[j];
c2[j] = 0;
}
}
printf("%.0f\n", c1[m]*F[m]);
}
return 0;
}
2.1.2 "红色病毒"问题
https://acm.hdu.edu.cn/showproblem.php?pid=2065
此题要将指数型母函数展成泰勒展开式,然后经过一系列化简最后可以用快速幂求得的式子,主要运用到了母函数的思想而非代码。
参考代码:
#include <bits/stdc++.h>
using namespace std;
#define MOD 100
int quickMul(int a, unsigned long long n)
{
int ans = 1;
a = a % MOD;
while(n)
{
if(n%2)
ans = (ans * a) % MOD;
a = (a * a) % MOD;
n = n/2;
}
return ans;
}
int main()
{
int t;
unsigned long long n;
while(scanf("%d", &t)!=EOF && t!=0)
{
for(int i=1; i<=t; i++)
{
scanf("%lld", &n);
int res = (quickMul(4, n-1) + quickMul(2, n-1)) % MOD;
printf("Case %d: %d\n", i, res);
}
printf("\n");
}
return 0;
}