您的位置:首页 > 其它

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

2015-02-21 16:08 351 查看
划分型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
[m]);

return 0;
}


总结:

1、虽说编程得站在机器的角度,但偶尔用人脑跑跑也不错;

2、动态规划就是记忆化的深搜;

3、记得不要考虑重复了!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: