妙用生成函数

还是从一道题目来开始生成函数的研究吧,下面的题目取自HDU_1028:


Problem Description

"Well, it seems the first problem is too easy. I will let you know how foolish you are later." feng5166 says. "The second problem is, given an positive integer N, we define an equation like this:

N=a[1]+a[2]+a[3]+...+a[m];

a[i]>0,1<=m<=N;

My question is how many different equations you can find for a given N. For example, assume N is 4, we can find:

4 = 4;

4 = 3 + 1;

4 = 2 + 2;

4 = 2 + 1 + 1;

4 = 1 + 1 + 1 + 1;

so the result is 5 when N is 4. Note that "4 = 3 + 1" and "4 = 1 + 3" is the same in this problem. Now, you do it!"

Input

The input contains several test cases. Each test case contains a positive integer N(1<=N<=120) which is mentioned above. The input is terminated by the end of file.

Output

For each test case, you have to output a line contains an integer P which indicate the different equations you have found.

Sample Input

4

10

20

Sample Output

5

42

627

可以这样考虑,一个自然数的一个可能的组合形式可以包含任意个1,任意个2,任意个3,... 等等组成。因此我们有G(x) = (1+x^1+x^2+x^3+...)(1+x^2+x^4+x^6)...(1+x^n+x^2n+...),该公式中的1可以看成x^0,不难得出,该生成函数展开后的形式中每一项的幂就代表一个自然数,其前的系数代表该自然数的组合形式有多少,我们模拟手工计算的方式写出程序:
#include<stdio.h>
#include<string.h>
int main(void)
{
    int arr[130];
    int temp[130];
    int n;
    while(scanf("%d", &n) != EOF)
    {
        for(int i = 0; i <= n; i++) // 初始化第1个表达式的系数;任何自然数都可以表示成若干个1的和
        {
            arr[i] = 1;
            temp[i] = 0;
        }
        for(int i = 2; i <= n; i++) // 从第2个表达式开始到第n个表达式
        {
            for(int j = 0; j <= n; j++) // 最前面表达式的各个系数为arr[i]            {
                for(int k = 0; k+j <= n; k += i) // 最前面的下一个表达式,指数增量为i
                    temp[k+j] += arr[j]; // 指数为j+k的系数为arr[k+j]加上最前面的那个表达式的指数为j的系数(即temp[j])
            }
            for(int i = 0; i <= n; i++) // 前i个表达式的乘积系数
            {
                arr[i] = temp[i];
                temp[i] = 0; // 临时数组清零以备下次使用
            }
        }
        printf("%d\n", arr[n]);
    }

    return 0;
}
注意到这个程序完全模拟了手工计算的过程,一个接一个的表达式相乘(排在最前面的那两个),并且忽略了我们不关心的部分,即大于n的情况没有计算。另外,测试可知,int型能放得下n = 120 的情况,因此我们可以不用long long int。额外的,如果在线评测系统测试数据量很大的话,上面的程序由于重复计算可能造成超时,因此可以采用预处理的方式,俗称打表法:
#include<stdio.h>
#include<string.h>
#define MAXN 120
int main(void)
{
    int arr[130];
    int temp[130];
    int n;

    for(int i = 0; i <= MAXN; i++)
    {
        arr[i] = 1;
        temp[i] = 0;
    }
    for(int i = 2; i <= MAXN; i++)
    {
        for(int j = 0; j <= MAXN; j++)
        {
            for(int k = 0; k+j <= MAXN; k += i)
                temp[k+j] += arr[j];
        }
        for(int i = 0; i <= MAXN; i++)
        {
            arr[i] = temp[i];
            temp[i] = 0;
        }
    }

    while(scanf("%d", &n) != EOF)
    {
        printf("%d\n", arr[n]);
    }

    return 0;
}
为了充分理解上面代码的含义,我们故技重施,再取一题,HDU_1398:

Problem Description

People in Silverland use square coins. Not only they have square shapes but also their values are square numbers. Coins with values of all square numbers up to 289 (=17^2), i.e., 1-credit coins, 4-credit coins, 9-credit coins, ..., and 289-credit coins, are available in Silverland.  There are four combinations of coins to pay ten credits: 
ten 1-credit coins,

one 4-credit coin and six 1-credit coins,

two 4-credit coins and two 1-credit coins,

and one 9-credit coin and one 1-credit coin. 
Your mission is to count the number of ways to pay a given amount using coins of Silverland.

Input

The input consists of lines each containing an integer meaning an amount to be paid, followed by a line containing a zero. You may assume that all the amounts are positive and less than 300.

Output

For each of the given amount, one line containing a single integer representing the number of combinations of coins should be output. No other characters should appear in the output.

Sample Input

2

10

30

0

Sample Output

1

4

27

同样地,我们写出其生成函数G(x) = (1+x^1+x^2+x^3+...)(1+x^4+x^8+x^12+...)...(1+x^298+x^596+...),写程序来模拟手工计算过程如下:

#include<stdio.h>
int main(void)
{
    int ans[310];
    int temp[310];
    int n;
    while(scanf("%d", &n) && n!=0)
    {
        for(int i = 0; i <= n; i++)
        {
            ans[i] = 1;
            temp[i] = 0;
        }
        for(int i = 2; i*i <= n; i++) // 另一个注意点,如果i <= n 的话,由于循环次数增多31MS才AC(i*i <= n 0MS_pass)
        {
            for(int j = 0; j <= n; j++)
            {
                for(int k = 0; k+j <= n; k += i*i) // 一个注意点
                    temp[k+j] += ans[j];
            }
            for(int i = 0; i <= n; i++)
            {
                ans[i] = temp[i];
                temp[i] = 0;
            }
        }
        printf("%d\n", ans[n]);
    }

    return 0;
}

打表法和上一题的程序中的打表法完全相同,不再赘述。


All Rights Reserved.
Author:海峰:)
Copyright © xp_jiang.
转载请标明出处:http://www.cnblogs.com/xpjiang/p/4430256.html以上.
时间: 2024-08-24 00:24:36

妙用生成函数的相关文章

看不懂的生成函数

不得不说这个东西真是妙啊 遭到了降智打击 生成函数又叫做母函数,主要用于解决一些组合数学问题 对于一个数列\(\{f_0,f_1,f_2,...,f_n\}\) 我们定义其生成函数为 \[F(x)=f_0+f_1x+f_2x^2+...+f_nx^n\] 也就是 \[F(x)=\sum_{i=0}^nf_ix^i\] 也就是把数列的每一项当成了多项式对应项的系数 $$$$ 既然是函数,那么我们就可以去计算对应的函数值 比如一个数列 \[\{1,1,1,1,1,1...\}\] 它的生成函数 \[

【BZOJ 3028】 3028: 食物 (生成函数)

3028: 食物 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 569  Solved: 382 Description 明明这次又要出去旅游了,和上次不同的是,他这次要去宇宙探险! 我们暂且不讨论他有多么NC,他又幻想了他应该带一些什么东西.理所当然的,你当然要帮他计算携带N件物品的方案数. 他这次又准备带一些受欢迎的食物,如:蜜桃多啦,鸡块啦,承德汉堡等等 当然,他又有一些稀奇古怪的限制: 每种食物的限制如下: 承德汉堡:偶数个 可乐:0个或1

BZOJ 3028 食物 ——生成函数

把所有东西的生成函数搞出来. 发现结果是x*(1-x)^(-4) 然后把(1-x)^(-4)求逆,得到(1+x+x^2+...)^4 然后考虑次数为n的项前的系数,就相当于选任意四个非负整数构成n的方案数. 大概就是C(n+3,3) 前面还有一项是x,所以n--即可. 然后就A掉了. #include <cstdio> #include <cstring> #define ll long long const int inv=1668; const int md=10007; in

《众妙之门——精通CSS3》一书知识点剖析

不得不佩服京东的速度,昨天刚下单的两本书今天上午就到了.其中一本是全彩页的<众妙之门 - 精通CSS3>,细看了前几十页,书上的叙述方式给我的印象其实不如“彩页”来的讨喜——接连说上几个例子,扔个例子的链接(没源码下载,要自己手动输入,而且近乎所有的例子页面均已失效),未经细剖便草草了事,感觉倒是适合作为厕所读物(汗).... 虽然书中不细剖,却也不能白花银子,不妨自己实打实地查资料.记笔记,不辜负了早上送出的老毛. P21 此处提到的例子动用了 -webkit-background-clip

System center 2012 R2 实战十、妙用SCO+SCOM实现声音告警

SCOM默认支持邮件告警,即时消息告警,短信告警,那么如果要实现声音告警,应该如何实现呢?大家可以好好思考一下,需求就是SCOM产生警报,同时触发声音告警+短信告警+邮件报警. 当时第一个想到的是Live Maps,Live Maps是一个单独的收费产品,通过Live Maps可以很好地与SCOM进行集成,还可以实现移动终端的声音告警,也可以做一套流程,比如说满足什么样子的条件,那些指定的服务器,去触发Sound声音插件,还可以实现地图展示,Live Maps有很多诱人的功能 产品地址如下 ht

as 的妙用

个人理解:as跟is is 相当于判断里的“==” 是与否 if(e.OriginalSource is Button) as 一般用来转换另一种object e.OriginalSource as Button 来获取相关属性或值 e.OriginalSource as RadioButton).Name.ToUpper() as和is的出现 一般是在路由事件中,需要通过这两个关键字来互交动态生成的不同类型或属性不同的UI控件 比如通过for生成按钮 for(int i = 0;i<10;i+

90%的卖家不知道,eBay销量爆涨有妙招!

说到提升销量,可能大部分卖家脑海里闪现的都是跟卖.优化.推广--,其实方法远远不止这些,今天小编讲讲不为人熟知的几种eBay销量爆涨妙招,希望对卖家们有所帮助. 1.UPC要好好利用 在eBay上,相同产品码的listing,是可以和Reviews共享客户的,而我们商家恰好可以利用这一点,借此找到那些Reviews数量多.质量好的listing,而如果它有产品码,而商家与该产品是同款,那我们也可以将产品码利用起来. UPC码正规渠道购买价格较高,如果是eBay用,商家可以去万能的某宝上购买,价格

转---深入浅出妙用 Javascript 中 apply、call、bind

作者:伯乐在线专栏作者 - chokcoco 如有好文章投稿,请点击 → 这里了解详情 如需转载,发送「转载」二字查看说明 这篇文章实在是很难下笔,因为网上相关文章不胜枚举. 巧合的是前些天看到阮老师的一篇文章的一句话: "对我来说,博客首先是一种知识管理工具,其次才是传播工具.我的技术文章,主要用来整理我还不懂的知识.我只写那些我还没有完全掌握的东西,那些我精通的东西,往往没有动力写.炫耀从来不是我的动机,好奇才是." 对于这句话,不能赞同更多,也让我下决心好好写这篇,网上文章虽多,

C++运算符重载的妙用

运算符重载(Operator overloading)是C++重要特性之中的一个,本文通过列举标准库中的运算符重载实例,展示运算符重载在C++里的妙用.详细包含重载operator<<,operator>>支持cin,cout输入输出.重载operator[],实现下标运算.重载operator+=实现元素追加:重载operator()实现函数调用.假设你对C++的运算符重载掌握的游刃有余.那就无需继续往下看了. 运算符重载带来的优点就是--让代码变得简洁.以下将展示几个标准库因使