HDU1028,解析母函数

F - Ignatius and the Princess III
Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u

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
 

 这是一道基本的母函数问题,具体什么是母函数和它的作用有多么强大,这在百度百科上说的很明白,我就不多说了,只说说母函数在程序里怎么用。

母函数问题都可以用模板解题的,可重点可能有很多人不理解模板的原理,特别是那三个for循环更是让人晕头转向,我也在网上看了好多讲解,也有我自己的理解,我们就那这个整数拆分的题来说:

首先由这道题我们能写出函数:(#式)  (1+x+x^2+x^3+x^4+x^5+....)*(1+x^2+x^4+x^6+x^8+x^10+....)*(1+x^3+x^6+x^9+x^12....).....

在这里说 i ,j,k分别是怎么回事吧。

首先要先用个for循环 把上面#式的第一个括号(1+x+x^2+x^3+x^4+x^5+....)的系数给放在c1中。(c1就是用来存放系数的,c2只是作为中间计算结果的储放,还是要放回到c1中的)

从而再次计算从 # 的 第二个括号开始 , 所以 i 就是代表的# 式第几个括号,

而 用程序模拟手工计算 , 就是 先计算第一个括号 与 第二个括号 计算 , 把结果放到c2中,

在把结果与第三个括号计算 , 把结果放到c2中 , 在和第四个括号计算,........

所以j 就是指的 已经计算出 的 各项的系数 ,比如第一次之后 1+x+x^2+x^3+x^4+x^5+... , j=0指向1 ,

j=1 指向x ,j=2 指向 x^2 .... , 而 k 就是指 将要计算的那个括号中的项 , 因为第i个括号 , 中的指数为0 , i , 2i....所以 k要 + i ;


而结果 c2[j+k] += c1[j]; 就是把 以计算出的 j项的系数 和 现在正在计算的括号的k项相乘 , 所以指数为j+k , 所以结果放到c2[j+k] 中 , 这就是这几个for的作用!(注意:为什么是c2[j+k]+=c1[j],因为目前所计算的第 i 个括号里的每一项的系数都是1,当该项乘以另外一项的时候,系数只取决于另外的一项的系数,而这里的另外一项的系数就是指数为 j 的项的系数,即c[j]。)


本题代码:

#include <stdio.h>
#include <string.h>
#include <math.h>
int main()
{
	int i,j,k,n,c[130],d[130];<span style="white-space:pre">		</span>//注意,题目中的c1和c2在这里分别用c和d代替
	while(scanf("%d",&n)!=EOF)
	{
	//	memset(c,1,sizeof(c));
		memset(d,0,sizeof(d));
		for(i=0;i<=n;i++)
			c[i]=1;<span style="white-space:pre">			</span>//这里不用<span style="font-family: 微软雅黑; ">memset(c,1,sizeof(c));</span>
		for(i=2;i<=n;i++)
		{
			for(j=0;j<=n;j++)
				for(k=0;k+j<=n;k+=i)
					d[k+j]+=c[j];
			memcpy(c,d,sizeof(d));
			memset(d,0,sizeof(d));
		}
		printf("%d\n",c[n]);
	}
	return 0;
}


下面的代码是带有注释,用来讲解的,更详细,大家可以看看:

#include <iostream>
using namespace std;
const int lmax=10000;
//c1是用来存放展开式的系数的,而c2则是用来计算时保存的,
//他是用下标来控制每一项的位置,比如 c2[3] 就是 x^3 的系数。 
//用c1保存,然后在计算时用c2来保存变化的值。 
int c1[lmax+1],c2[lmax+1];
int main()
{
            int n, i, j, k ;
           // 计算的方法还是模拟手动运算,一个括号一个括号的计算,
           // 从前往后 
           while ( cin>>n )

          {
                     //对于 1+x+x^2+x^3+ 他们所有的系数都是 1 
                     // 而 c2全部被初始化为0是因为以后要用到 c2[i] += x ; 
                     for ( i=0; i<=n; i++ )

                     {
                                c1[i]=1;
                                c2[i]=0;
                     }
                      //第一层循环是一共有 n 个小括号,而刚才已经算过一个了
                      //所以是从2 到 n 
                     for (i=2; i<=n; i++)

                   {
                                 // 第二层循环是把每一个小括号里面的每一项,都要与前一个
                                 //小括号里面的每一项计算。 
                                for ( j=0; j<=n; j++ )
                                 //第三层小括号是要控制每一项里面 X 增加的比例 
                                 // 这就是为什么要用 k+= i ; 
                                         for ( k=0; k+j<=n; k+=i )

                                        {
                                                 // 合并同类项,他们的系数要加在一起,所以是加法,呵呵。 
                                                 // 刚开始看的时候就卡在这里了。 
                                                 c2[ j+k] += c1[ j];
                                         }
                               // 刷新一下数据,继续下一次计算,就是下一个括号里面的每一项。 
                              for ( j=0; j<=n; j++ )

                              {
                                          c1[j] = c2[j] ;
                                          c2[j] = 0 ;
                              }
                   }
                    cout<<c1[n]<<endl;
        }
         return 0;
}




  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值