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

    xiaoxiao2021-04-11  39

    题目描述:

    现在有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];

    }

    转载请注明原文地址: https://ju.6miu.com/read-666683.html

    最新回复(0)