生成函数与母函数

目录​​​​​​​

 一、参考博客:

二、题目情景(模板):

三、例题

例题一:

例题二:


 一、参考博客:

母函数(对于初学者的最容易理解的)_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思考难度可能比较大,母函数只需要套用模板就可以。

例题一:

3417. 砝码称重 - AcWing题库

母函数做法(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;
}


例题二:

P1164 小A点菜 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

母函数做法:由于本题卡时间,母函数方法会有两个点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;
}

posted @   光風霽月  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示