找单词

题目链接HDU-2082


题目描述

Problem Description
假设有x1个字母A, x2个字母B,….. x26个字母Z,同时假设字母A的价值为1,字母B的价值为2,….. 字母Z的价值为26。那么,对于给定的字母,可以找到多少价值<=50的单词呢?单词的价值就是组成一个单词的所有字母的价值之和,比如,单词ACM的价值是1+3+14=18,单词HDU的价值是8+4+21=33。(组成的单词与排列顺序无关,比如ACM与CMA认为是同一个单词)。

Input
输入首先是一个整数N,代表测试实例的个数。
然后包括N行数据,每行包括26个<=20的整数x1,x2,…..x26.


思路

题意很明显了,就是告诉你一个数字,询问有多少种方式组成这个数;

我们这里运用母函数求解;

如果你不了解母函数,以下为推荐的两篇学习母函数的博客
母函数学习
母函数学习2

母函数在我的理解就是把组合问题的加法法则和幂级数的乘幂对应起来,这样就可以解决一些问题;

到最后,指数的大小就是(价值,质量等),系数就是方案数;

当然,他可以优化一下,就是记录每个式子当前的最高次幂和上一次的最高次幂;
可以节省一定的时间;

在本题中,我们用v数组保存每个字母的价值,然后n1表示最小选择的数量,这里默认为0,n2表示当前字母最多有多少种选择(即上限).c1保存的是多项式的乘积。初始化只有一项 (1) . 然后c2每次存储下一项与之前项的乘积;
最后c1[n]存储的就是生成价值n的方案数;

最后我们统计价值1 - 50的方案数即可;

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <bits/stdc++.h>
using namespace std;

#define MAX 50
#define N 26

int c1[MAX + 10], c2[MAX + 10], v[N], n1[N], n2[N];

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
memset(n1, 0, sizeof(n1));
for(int i = 0; i < N; i++)
v[i] = i + 1;
cin >> t;
while(t--)
{
for(int i = 0; i < N; i++)
cin >> n2[i];
memset(c1, 0, sizeof(c1));
memset(c2, 0, sizeof(c2));
c1[0] = 1;
for(int i = 0; i < N; i++)
{
for(int j = n1[i]; j <= n2[i] && j * v[i] <= MAX; j++)
{
for(int k = 0; k + j * v[i] <= MAX; k++)
{
c2[k + j * v[i]] += c1[k];
}
}
for(int j = 0; j <= MAX; j++)
{
c1[j] = c2[j];
c2[j] = 0;
}
}
int ans = 0;
for(int i = 1; i <= 50; i++)
if(c1[i])
ans += c1[i];
cout << ans << endl;
}
return 0;
}
-------------本文结束,感谢您的阅读!-------------