划分型DP第二题,wikioi 1039,与第一题乘积最大思路有所不同。
题目要求:
将一个数划分成几部分,问一共有多少种分法。
真 · 解题思路:
其实跟小学奥赛的一些题有点像,我们先来看一个例子:
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 <= j/i )
也可以化简一下,因为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、记得不要考虑重复了!
原文地址:http://blog.csdn.net/fuyukai/article/details/43898639