LeeBlog

导航

HDU 1028 Ignatius and the Princess III 母函数 模板 or 完全背包

这题是母函数的一模板题,如果你还不知道什么叫母函数,请去网上下载卢华明,卢开澄的<组合数学>,或者去tankywoo那里把原理看懂,http://www.wutianqi.com/?p=596。里面的第二章把母函数的原理分析得很透彻,下面讲代码实现.其实下面代码就是电脑模拟人工计算,而且很有规则,是从第一个括号开始算起,先把第一个括号跟第二个括号相乘,然后这两个括号就合并了,然后又把第一个括号跟第二个括号相乘,知道n个括号全部被算出来( i 就代表第几个括号正在被合并 );我想大家肯定对m2[j + k] += m1[j]很疑惑,是的,我也在这卡了很久.我开始总以为这是什么公式,结果一直没想通,后来按一个括号一个括号合并老老实实用笔算才发现,j原来是代表大括号中的第j项,而k就代,大括号后面那个括号中的第k项( 其他结题报告可能把k叫做第i个括号中的第k项,那样虽然更书面,但是我觉得这样更好理解 ).为什么是k = k + i   呢?请听下面分解,因为大括号后面那个括号中每两项之间相差x^i,所以k每次都要加i,这样大括号中第j项乘的就全是后面那个括号中的,值赋给m2[j + k]是因为第j项,第k项相乘后就得到第j+k项,j+k<n就更好理解了,很明显下标不能超过n.

#include<stdio.h>
int m1[124],m2[124];//m1[i]记录x次数位i的系数,m2[]是一个中转的便于计算 
void mn( int n )
{
	for( int i = 0; i <= n; ++i )
	{
		m1[i] = 1;
		m2[i] = 0;
	}
	for( int i = 2; i <= n; ++i ) //难点
	{//总共有n个括号,从第2个起每一个括号都要和前面那一个括号相乘
	//所以可以忽略第一个括号 
		for( int j = 0; j <= n; ++j )//j代表最前面这个大括号的项数 
			for( int k = 0; k + j <= n; k += i )//在大括号后面,x都是以i方递增的 
				m2[j + k] += m1[j];//这里就是大括号后面的括号与前面相乘的计算 
		for( int j = 0; j <= n; ++j )
		{//算完以后都存在m2里面,所以要把值赋给m1, 
			m1[j] = m2[j];
			m2[j] = 0;
		}
	}
}
int main( )
{
	mn( 123 );
	int n;
	while( scanf( "%d",&n ) != EOF )
		printf( "%d\n",m1[n] );//x^n的系数就代表和为n的组合数 
	return 0;
}

其实母函数的原型是下面这个,仔细想想就懂了,出了1028这种类型外,其他想像2079http://www.cnblogs.com/Lvsi/archive/2011/05/11/2043707.html这种类型,要从第一个括号开始

#include<stdio.h>
int m1[124],m2[124];
void chart( )
{
     for( int i = 0; i < 124; ++i )
          m1[i] = 0,m2[i] = 0;
     m1[0] = 1;
     for( int i = 1; i < 124; ++i )
     {
          for( int j = 0; j < 124; ++j )
               for( int k = 0; j + k < 124; k += i )
                    m2[j + k] += m1[j];
          for( int j = 0; j < 124; ++j )
               m1[j] = m2[j],m2[j] = 0;
      }
 }
int main( )
{
    chart();
    int n;
    while( scanf( "%d",&n ) != EOF )
           printf( "%d\n",m1[n] );
    return 0;
}

完全背包做法

#include<stdio.h>
#include<string.h>
int dp[125],n;
void DP( )
{
     memset( dp,0,sizeof( dp ) );
     dp[0] = 1;
     n = 124;
     for( int i = 1; i <= n; ++i )
          for( int j = i; j <= n; ++j )
               dp[j] += dp[j-i];
 }
int main( )
{
    DP();
    while( scanf( "%d",&n ) != EOF )
           printf( "%d\n",dp[n] );
    return 0;
}

posted on 2011-03-26 20:44  LeeBlog  阅读(442)  评论(2编辑  收藏  举报