面试经典动态规划,n个相同火柴分堆

来源:互联网 发布:深度卸载mac上的软件 编辑:程序博客网 时间:2024/05/16 18:35

题目描述:

现在有n个相同火柴,将这些火柴分成若干堆,问所有的分法总数。

题目分析:

初见题目第一反应是采用组合数学的方法来计算,有种叫Stirling数的组合数。他表示将n个不同物品分到k堆里,注意这里是n个不同物品,所以不能采用stirling数。

那么我们简单的做几个尝试好了,假设n=4,那么可以分如下几种:

4

1,3和 3,1算一种

2,2 

1,1,2和 1,2,1 和 2,1,1算一种

1,1,1,1

这5个序列看似没有规律,但是我们还是能找出规律的,(1)中最大堆的根数是4,其他堆的总和是0;(2)中最大堆的根数是3,

其他堆的总和是1;(3)(4)中最大堆的根数是2,其他堆的总和是2,其他堆中的最大也不超过2;(5)中最大堆的根数是1,其他堆的

根数的总和是3,其他堆的最大根数也不超过1。

所有的情况综合起来就是:4个火柴分堆,有可能最大堆的根数是4,其他堆的总和是0,最大根数也是4。或者最大堆的根数不是4

而是3,就属于n个火柴分堆,但最大堆的根数不超过3这种情况了。然后依次类推。

所以可以总结递推公式是dp(n,k) = dp(n-k,k) + dp(n,k-1)。这里n是火柴总数,k表示分堆时最大堆的根数是k。dp(n,k)表示所有分

法的总数,dp(n-k,k)表示确实有分出去一个堆,该堆的根数是k的所有分法,那么剩下还没有分的火柴是n-k根。dp(n,k-1)表示,所有

没有堆的根数到达k(就是所有堆的根数都<=k-1)的所有分法。

那么我们的目标就是求dp(n,n)。在此我们添加边界条件,dp(0,i) = 1,i >= 0;dp(j,0) = 0, j > 0;

伪代码:

for(i = 0; i <= n; .i++)dp[0][i] = 1;

for(j = 1; j <= n ; j++)dp[j][0] = 0;

for(i = 1; i <= n ; i++)

for(j = 1; j <= i; j++)

{

int tmp = 0;

if(i - j > j)tmp = dp[i-j][di-j];

else tmp = dp[i-j][j];

dp[i][j] = tmp + dp[i][j - 1];

}

0 0
原创粉丝点击