题目链接:HDU-2079
题目描述:
Problem Description
又到了选课的时间了,xhd看着选课表发呆,为了想让下一学期好过点,他想知道学n个学分共有多少组合。你来帮帮他吧。(xhd认为一样学分的课没区别)
Input
输入数据的第一行是一个数据T,表示有T组数据。
每组数据的第一行是两个整数n(1 <= n <= 40),k(1 <= k <= 8)。
接着有k行,每行有两个整数a(1 <= a <= 8),b(1 <= b <= 10),表示学分为a的课有b门。
思路
题意很明显了,就是告诉你一个数字,询问有多少种方式组成这个数;
我们这里运用母函数求解;
如果你不了解母函数,以下为推荐的两篇学习母函数的博客
母函数学习
母函数学习2
母函数在我的理解就是把组合问题的加法法则和幂级数的乘幂对应起来,这样就可以解决一些问题;
到最后,指数的大小就是(价值,质量等),系数就是方案数;
当然,他可以优化一下,就是记录每个式子当前的最高次幂和上一次的最高次幂;
可以节省一定的时间;
在本题中,我们用v数组保存每个课的价值(也就是学分),然后n1表示最小选择的数量,这里默认为0,n2表示当前课最多有多少种选择(即上限).c1保存的是多项式的乘积。初始化只有一项 (1) . 然后c2每次存储下一项与之前项的乘积;
最后c1[n]存储的就是生成学分n的方案数;
代码
#include <bits/stdc++.h>
using namespace std;
#define MAX 50
#define N 10
int c1[MAX], c2[MAX], v[N], n1[N], n2[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
memset(n1, 0, sizeof(n1));
int t;
cin >> t;
while(t--)
{
int n, k;
cin >> n >> k;
for(int i = 0; i < k; i++)
{
cin >> v[i] >> n2[i];
}
memset(c1, 0, sizeof(c1));
memset(c2, 0, sizeof(c2));
c1[0] = 1;
for(int i = 0; i < k; i++)
{
for(int j = n1[i]; j <= n2[i] && j * v[i] <= n; j++)
{
for(int k = 0; k + j * v[i] <= n; k++)
{
c2[k + j * v[i]] += c1[k];
}
}
for(int j = 0; j <= n; j++)
{
c1[j] = c2[j];
c2[j] = 0;
}
}
cout << c1[n] << endl;
}
return 0;
}