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; }