算法设计(动态规划)| 青训营笔记

105 阅读1分钟

这是我参与「第四届青训营 」笔记创作活动的第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)