首页 > 代码库 > 动态规划 以及相应的实例

动态规划 以及相应的实例

动态规划(dynamic programming)是通过组合子问题的解而解决整个问题的,这里的programming是一种规划,DP适用于子问题不是独立(俩个子问题不共享资源即是独立)的问题,即各个子问题包含公共子问题,DP只对于子问题求解一次,并将求解的值保存在一张表中,避免了重复求解相同子问题的操作。DP通常用于最优化的问题,通常是一个问题有很多的可行解,每一个解含有一个值,我们希望找到一个最优的解(最大或是最小)。

DP一般设计的4个步骤:

1.描述最优解的结构

2.递归定义最优解的值

3.按照自底向上的方式求解最优解的值

4.有计算结果构造最优解

DP性质:对于一个问题是否可以用DP解决是需要判断的,如果一个问题的最优解当中包含了子问题的最优解,即该问题具有最优子结构性质,提示我们用DP可能会解决问题。而当一个递归算法不断调用同一问题的时候,此时该最优问题包含了重叠子问题。同时满足二者,此时我们可以用DP去求解问题。

下面针对于一个简单的DP小例子进行描述:

数字三角形问题,问题描述:

对于输入一个数字三角形,每行的整数个数对应其行数,在三角形中需找一条从顶到底的路径,使得路径上的数字之和最大,输出最大值,三角形行数小于100,路径上的每一步只可以向下或右下去走。给出一个问题的样例:


求出上述例子的最大解?

分析:

上述数据可以用一个二维数组进行描述,即

D[i][j] 第i行第j个数 从D[i][j]下一步走D[i+1][j]或D[i+1][j+1] Max[i][j] 表示从D[i][j]到底端的路径中最佳路径数字之和。
如果只有一行的时候 Max[i][j] = D[i][j],否则Max[i][j] = max(Max[i+1][j],Max[i+1][j+1])+D[i][j];


下面给出递推型DP:

#include <iostream>
#include <algorithm>
#include <cstdio>

using namespace std;
const int num = 101;
int D[num][num];
int n;
int Max[num][num];
int main()
{
    int i,j;
    cin>>n;
    for(i=1;i<=n;i++)
        for(j=1;j<=i;j++)
            cin>>D[i][j];

    for(int k=1;k<=n;k++)
        Max[n][k] = D[n][k];

    for(int i=n-1;i>=1;i--){
        for(int j=1;j<=i;j++)
            Max[i][j] = max(Max[i+1][j],Max[i+1][j+1])+D[i][j];
    }
    cout<<Max[1][1]<<endl;

    return 0;
}
输出的结果即为:30

上述算法时间复杂度o(n^2),空间复杂度o(n^2),有图表可知,最后的有很大的内存空间是浪费的,并不会全部利用上,当行数很大的时候浪费明显,我们可以将计算出来的每个子问题的解存放在一个一维数组中,覆盖性的写入,从而最后的解即为数组的头元素。空间复杂度即为o(n).

演示如下图:


即为所求解。

#include <iostream>
#include <algorithm>
#include <cstdio>

using namespace std;
const int num = 101;
int D[num][num];
int n;
int *Max;
int main()
{
    int i,j;
    cin>>n;
    for(i=1;i<=n;i++)
        for(j=1;j<=i;j++)
            cin>>D[i][j];

        Max = D[n];

    for(int i=n-1;i>=1;i--){
        for(int j=1;j<=i;j++)
            Max[j] = max(Max[j],Max[j+1])+D[i][j];
    }
    cout<<Max[1]<<endl;

    return 0;
}
最长公共子串:

俩个字符串s1,s2,用maxlen(i,j)表示s1左边i个字符,s2左边j个字符的最长公共子序列长度。(解的状态)

如果俩个子串任何一个为空,公共子串的长度为0;代码如下:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
char s1[100];
char s2[100];
int maxlen[100][100];

using namespace std;

int main()
{
    while(cin>>s1>>s2){
        int len1 = strlen(s1);
        int len2 = strlen(s2);
        int i,j;
        for(i=0;i<=len1;i++)
            maxlen[i][0] = 0;
        for(j=0;j<=len2;j++)
            maxlen[0][j] = 0;
        for(i=1;i<=len1;i++){
            for(j=1;j<=len2;j++){
                if(s1[i-1]==s2[j-1])
                    maxlen[i][j] = maxlen[i-1][j-1]+1;
                else
                    maxlen[i][j] = max(maxlen[i-1][j],maxlen[i][j-1]);
            }

        }
         cout << maxlen[len1][len2] << endl;
    }
    return 0;
}
最长公共子串并不要求,子串是连续的。