生成函数与母函数
目录
一、参考博客:
母函数(对于初学者的最容易理解的)_yu121380的博客-CSDN博客_母函数c++
母函数(Generating function)详解 --- TankyWoo (wutianqi.com)
二、题目情景(模板):
给定n个物品,每个物品的价值是val,数量是num,现在让你求满足总价值是m的方案数
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int N = 110;
int num[N], val[N], s[N], t[N];
int m, n, maxn;
int main()
{
cin >> n >> m; //n个物品
for(int i = 1; i <= n; i ++ )
{
cin >> val[i] >> num[i];
maxn += val[i] * num[i]; //maxn表示最大价值
}
sort(val + 1, val + n + 1); //可有可无
for(int i = 0; i <= num[1] * val[1]; i += val[1])//用最小的那个val初始化
s[i] = 1;
for(int i = 2; i <= n; i ++ ) //遍历后面所有的多项式b,用b的每一项和当前多项式a相乘
{
for(int j = 0; j <= maxn; j ++ ) //遍历当前多项式a的每一项
{
for(int k = 0; k + j <= maxn && k <= num[i] * val[i]; k += val[i]) //遍历多项式b的每一项
t[k + j] += s[j]; //系数是由当前多项式a的系数决定的,b的每一项的系数肯定都是1
}
for(int j = 0; j <= maxn; j ++ ) //注意不要写成i的循环,i变量此时还在使用
{
s[j] = t[j];
t[j] = 0;
}
}
cout << s[m] << endl;
return 0;
}
三、例题
说明:能用母函数解决的问题,dp都可以解决,dp的代码更为简洁,但是dp思考难度可能比较大,母函数只需要套用模板就可以。
例题一:
母函数做法(AC):
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int N = 500010;
int num[N], val[N], n;
int s[N], t[N], maxn;
int main()
{
cin >> n;
for(int i = 0; i < n; i ++ ) //n个石子,每个石子的重量是val,数量是num
{
cin >> val[i];
num[i] = 1;
maxn += val[i] * num[i];
}
sort(val, val + n);
for(int i = 0; i <= val[0] * num[0]; i += val[0]) s[i] = 1;
for(int i = 1; i < n; i ++) //每一个多项式
{
for(int j = 0; j <= maxn; j ++) //当前(前面的)多项式的每一项
{
for(int k = 0; k <= val[i] * num[i] && k + j <= maxn; k += val[i]) //该多项式可以取到的值
{
t[k + j] += s[j];
t[abs(k - j)] += s[j];
}
}
for(int j = 0; j <= maxn; j ++ ) s[j] = t[j], t[j] = 0;
}
int cnt = 0;
for(int i = 1; i <= maxn; i ++ )
if(s[i])
cnt ++ ;
cout << cnt << endl;
return 0;
}
dp做法:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110, M = 2e5 + 10, B = M / 2;
int w[N], n;
bool f[N][M];
int main()
{
cin >> n;
int m = 0;
for(int i = 1; i <= n; i ++ ) cin >> w[i], m += w[i];
f[0][B] = true;
for(int i = 1; i <= n; i ++ )
{
for(int j = -m; j <= m; j ++ )
{
f[i][j + B] = f[i - 1][j + B]; //不选
if(j - w[i] >= -m) f[i][j + B] |= f[i - 1][j - w[i] + B]; //选在左边
if(j + w[i] <= m) f[i][j + B] |= f[i - 1][j + w[i] + B]; //选在右边
}
}
int res = 0;
for(int i = 1; i <= m; i ++ )
if(f[n][i + B]) res ++ ;
cout << res << endl;
return 0;
}
例题二:
母函数做法:由于本题卡时间,母函数方法会有两个点TLE,得82分
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int N = 110;
int num[N], val[N];
int m, n, s[N], t[N], maxn;
int main()
{
cin >> n >> m;
maxn = m;
for(int i = 1; i <= n; i ++ )
{
cin >> val[i];
num[i] = 1;
}
sort(val + 1, val + n + 1);
s[0] = s[val[1]] = 1;
for(int i = 2; i <= n; i ++ ) //遍历所有的多项式
{
for(int j = 0; j <= maxn; j ++ ) //遍历当前的所有价格
{
for(int k = 0; k + j <= maxn && k<= num[i] * val[i]; k += val[i])
{
t[k + j] += s[j];
}
}
for(int j = 0; j <= maxn; j ++ ) //不要写成i的循环,前面已经使用了i的变量
{
s[j] = t[j];
t[j] = 0;
}
}
cout << s[maxn] << endl;
return 0;
}
正解(dp):
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110, M = 10010;
int f[N][M], n, m, w[N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i ++ ) cin >> w[i];
f[0][0] = 1;
for(int i = 1; i <= n; i ++ )
{
for(int j = 0; j <= m; j ++ )
{
if(j >= w[i]) f[i][j] = f[i - 1][j] + f[i - 1][j - w[i]];
else f[i][j] = f[i - 1][j];
}
}
cout << f[n][m] << endl;
}
dp优化:
#include<bits/stdc++.h>
using namespace std;
int m, n, a, dp[10010];
int main()
{
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; ++ i)
{
scanf("%d", &a);
for(int j = m; j > a; --j)
dp[j] += dp[j - a];
++dp[a];
}
printf("%d", dp[m]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!