动态规划学习系列——划分DP(二)

划分型DP第二题,wikioi 1039,与第一题乘积最大思路有所不同。

题目要求:
将一个整数N划分成K部分,问一共有多少种分法。
真 · 解题思路
其实跟小学奥赛的一些题有点像,我们先来看一个例子:
7 分成 3 部分,有四种办法:
1,1,5;1,2,4;1,3,3;2,2,3 。
我们人脑来考虑问题的时候是怎么想的呢?显然,从1开始,1是第1部分,然后剩下6分成2部分,我们依然是从1开始考虑,1是第2部分,然后5是第3部分;同样接下来考虑第2部分是2,则第3部分是4;接着考虑第2部分是3,第3部分是3;因为每一部分不能比前面的部分小(避免重复),所以1为第1部分的就考虑完了,然后考虑第1部分是2,同理,从第2部分是2开始,于是只有2,3这种情况。
上述人脑思路有点像搜索,这也是为什么我喜欢深搜算法的原因。
但是,现在我们不想用搜索来做,而是用动态规划来做,我们又该怎么考虑呢?
某大牛的名言:动态规划 == 记忆化搜索
我们又知道,动态规划是用上一个状态来推出当前状态的,那么我们可以这么来考虑:单独把第1部分拿出来,考虑第1部分的所有情况以及对应的所有剔除第一部分分成m-1部分的情况,于是,有状态转移方程
dp[i][j] = ∑dp[i-k][j-1] ( 1 <= k <= i/j )
也可以化简一下,因为dp[i-1][j-1] = ∑dp[i-k-1][j-1]:
dp[i][j] = dp[i-1][j-1] + dp[i-j][j]
这个公式可以理解为:先单独考虑第1部分是1,剩下的第1部分必定大于1,所以每一部分都减1也无妨,这个值也是我们知道的!

代码:

#include <bits/stdc++.h>

using namespace std;

int n,m,dp[205][10];

int main()
{
    scanf("%d %d",&n,&m);
    dp[0][0]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(j<=i)
                dp[i][j]=dp[i-j][j]+dp[i-1][j-1];
        }
    }
    printf("%d\n",dp[n][m]);

    return 0;
}

总结:
1、虽说编程得站在机器的角度,但偶尔用人脑跑跑也不错;
2、动态规划就是记忆化的深搜;
3、记得不要考虑重复了!

你可能感兴趣的:(dp,动态)