这是我参与「第四届青训营 」笔记创作活动的第4天
动态规划
1.题目描述
将整数 n 分成 k 份,且每份不能为空,任意两个方案不相同(不考虑顺序)。
例如:n=7,k=3,下面三种分法被认为是相同的。
1,1,5
1,5,1
5,1,1
问有多少种不同的分法。
输入格式
n,k(6<n<=200,2<=k<=6)
输出格式
共一行,包含一个数值,表示不同的分法
样例
输入:7 3
输出:4
2.分析
从题面的描述上看就很像动态规划题。
状态表示:f[i][x]表示i分成x个非空的数的方案数
显然i<x时,f[i][x]=0,i=x时,f[i][x]=1
剩下的情况我们分情况讨论:
①有1的 ②没有1的
第一种情况,方案数为 f[i-1][x-1]
第二种情况,方案数为 f[i-x][x] (此时 i 必须大于 x)
状态转移
f[i][x]=f[i-1][x-1]+f[i-x][x]
3.证明
显然,状态转移方程把合法方案的集合分成了不重不漏的两部分:即划分中有1或无1的情况。这样就可以枚举出所有可能成为最优方案的合法方案,得到最优解。
4.代码
#include<iostream>
using namespace std;
int n,k,f[201][7]; //f[k][x] k 分成 x 份 ={f[k-1][x-1],f[k-x][x]}
int main()
{
cin >> n >> k;
for (int i=1;i<=n;i++)
{
f[i][1]=1;f[i][0]=1;
}
for (int x=2;x<=k;x++)
{
f[1][x]=0;
f[0][x]=0;
} // 边界,为了防止炸,我把有0的也处理了
for (int i=2;i<=n;i++)
{
for (int x=2;x<=k;x++)
{
if (i>x)
f[i][x]=f[i-1][x-1]+f[i-x][x];
else
f[i][x]=f[i-1][x-1];
}
}
cout<<f[n][k]<<endl;
return 0;
}
5.复杂度分析
代码主体部分for循环嵌套,外层是n,内层是k,所以循环次数nk,则总时间复杂度是O(n*k)