码迷,mamicode.com
首页 > 其他好文 > 详细

妙用生成函数

时间:2015-04-16 00:58:01      阅读:151      评论:0      收藏:0      [点我收藏+]

标签:

还是从一道题目来开始生成函数的研究吧,下面的题目取自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
以上.

妙用生成函数

标签:

原文地址:http://www.cnblogs.com/xpjiang/p/4430256.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!