HDU 1028 Ignatius and the Princess III (母函数 或 动态规划DP)

来源:互联网 发布:中世纪2优化9神罗 编辑:程序博客网 时间:2024/05/20 16:32

传送门:HDU 1028



Sample Input
41020

Sample Output
542627



题目大意

对于给定的数N,问N有多少种不同的拆分方式。如:4=4 , 4=3+1 , 4=2+2 ,4=2+1+1 ,4=1+1+1+1 ,共5种不同的拆分方式,注意 4=1+3和4=3+1是同一种。1<=N<=120.



前置技能

1.拆分数: 整数拆分就是把正整数 分解成若干正整数的和。不同的拆分方法的总数叫做拆分数。

2.母函数:关于母函数的讲解可以参考我写的文章普母函数详解。


这个题大约有两种做法,一种是母函数,一种是动态规划,个人感觉母函数的思路要简单一点,就是套模板,但是代码较动态规划的要多一点。



母函数版思路

N最大为120,则N最多可以由N个数组成,即N个1组成,最少可以由一个数N组成。则可以把题目转化为,现在有值为1~120的120个数,每个数最多有120个,问N可以由给出这些数的不同的组成方式。这样就转化为了求组合数的问题,就可以用普通型母函数来解决了。



具体实现

值为 i 的数就对应着 x^i ,即v[i]=i;每个数值的数最少有0个,则s[i]=0;每个数值的数最多有120个,则e[i]=120;初始化完毕后直接调用母函数的模板,然后输出结果就好。


#include<stdio.h>#include<string.h>#define MAXN 122int a[MAXN],b[MAXN]; //a存储最终结果,b存储中间结果 int s[MAXN],e[MAXN],v[MAXN];//s为第i个数最少的个数,e为第i个数最多的个数,v为第i个数的数值 //直接调用母函数模板 void mu(int n){ //n为因子个数 int i,j,k;memset(a,0,sizeof(a));a[0]=1;for(i=1;i<=n;i++){memset(b,0,sizeof(b));for(j=s[i];j<=e[i]&&j*v[i]<=MAXN;j++)for(k=0;k+j*v[i]<=MAXN;k++)b[k+j*v[i]]+=a[k];memcpy(a,b,sizeof(b));}}int main(){int i,n;//先初始化s、e、v数组 memset(s,0,sizeof(s));for(i=0;i<122;i++) {e[i]=120;v[i]=i;}mu(120);while(~scanf("%d",&n)){ //a[i]存的是x^v[i]前的系数,即第i个数(数值为v[i]的数)的拆分数 printf("%d\n",a[n]);}return 0;}

动态规划版思路

我们用dp[n][k]表示用若干最大不超过k的数组成数字n的不同方法数。则可以得到动态转移方程:dp[n][k]=dp[n][k-1]+dp[n-k][k]

也就是如果组成n的若干数中没有用到数字k,只用了比k小的数,则方法有dp[n][k-1]种;如果组成n的若干数中用到了数字k,也就是最大数为k,则先从n中拿出一个值为k的数,就保证了一定有k,剩余值为n-k的数可以有不超过k的若干数组成,有dp[n-k][k]种。将两者相加即可。


边界条件

dp[n][1]=1,dp[1][n]=1,dp[0][n]=1 ,注意当dp[n][m]中m>n时,dp[n][m]=dp[n][n]


#include<stdio.h>#include<string.h>int main(){int i,j,n;int dp[122][122];memset(dp,0,sizeof(dp));for(i=1;i<=120;i++){dp[i][1]=1;dp[1][i]=1;dp[0][i]=1;}for(i=2;i<=120;i++)for(j=2;j<=120;j++)if(j>i) dp[i][j]=dp[i][i];else dp[i][j]=dp[i][j-1]+dp[i-j][j];while(~scanf("%d",&n)){printf("%d\n",dp[n][n]);}return 0;}

阅读全文
1 0
原创粉丝点击