动态规划 以及相应的实例

动态规划(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;
}

最长公共子串并不要求,子串是连续的。

动态规划 以及相应的实例

时间: 2024-11-06 15:22:42

动态规划 以及相应的实例的相关文章

动态规划算法实例三则

动态规划属于不好理解的计算机基本算法之一. 需要经过多次实践,才能体会其精妙之处. 其精妙的地方在于:降低运算量. 下面通过实例理解动态规划解题思路. 实例一:求数组的最大连续和子数组.参考文章 用动态规划来解,首先得考虑状态和状态转移方程.如果我们把题述数组看成序列,那么是不是可以用序列DP来考虑呢? 我们不妨考虑一个这样的序列:1,-3,5,-2,4 a[i]表示这个序列的第 i 个元素,dp[i]表示最后一个元素是a[i]的最大连续和(此乃状态,是不是跟LIS的DP解法有点类似),于是:

算法学习笔记(八) 动态规划的一般求解方法

1 一个问题:换零钱方式的统计 SICP 第一章 1.2.2 树形递归中,有这么一问题:给了半美元,四分之中的一个美元.10美分,5美分和1美分的硬币.将1美元换成零钱,一共同拥有多少种不同方式?更一般的问题是,给定了随意数量的现金,我们能写一个程序,计算出全部换零钱方式的种数吗? 2 动态规划的基本模型 动态规划(Dynamic programming,DP),是研究一类最优化问题的方法,通过把原问题分解为相对简单的子问题的方式求解复杂问题.动态规划处理的也就是是多阶段决策最优化问题,这一类问

动态规划的设计思想与实例(最大子段和、最长公共子序列、0-1背包、编辑距离)

动态规划算法与分治法类似,其基本思想是将总问题分解成若干个子问题,先求解子问题,再结合这些子问题的解得到原问题的解.与分治法不同的是,动态规划求解的问题经分解得到的子问题往往不是相互独立的. 基本思想: 将总问题分解成多个子问题(子问题也可以继续分解,直到无法分解),计算子问题,用一个表保存已解决的子问题的答案,算完子问题后回到总问题时从表中寻找已求得的答案,根据要求挑选最优解,加上总问题的里的具体变化再存入表中 设计步骤: 1.找出最优解的性质,并刻画其结构特征 根据具体问题找出其结构的特点,

高级算法——动态规划(斐波那契函数实例)

//使用递归去解决问题虽然简洁, 但效率不高,转为动态规划较好 function recurFib(n) {//斐波那契数列——递归 if (n <= 2) { return 1; } else { return recurFib(n - 1) + recurFib(n - 2); } } function dynFib(n) {//斐波那契数列——动态规划 var val = []; if (n == 1 || n == 2) { return 1; } else { val[1] = 1;

动态规划问题以及诸多实例分析

开始,我先完整的分析一个动态规划问题,叫做钢条切割问题,这个问题从递归开始导入,然后引入带备忘录的自顶向下方法,最后得到动态规划的解法.所有的问题都可以遵循这样的解决方法.然后开始分析如何用递归求解动态规划问题,最后分析如何使用动态规划的解法. 钢条切割问题: 问题描述,给定一个数组,表示的是出售长度为i的钢条的价格.如p = [1, 5, 8, 9, 10, 17, 17, 20, 24, 30] 表示的是长度为1的钢条为1美元,长度为2的钢条为5美元,以此类推. 现在有一个钢条长度为n,那么

动态规划总结

动态规划通常应用于最优化问题,即要做出一组选择以达到一个最优解.在做选择的同时,经常出现同样形式的子问题.当某一特定的子问题可能出自于多于一种选择的集合时,动态规划是很有效的.关键技术是存储这些子问题每一个的解,以备它重复出现. 和分治法一样,动态规划是通过组合子问题的解而解决整个问题的.动态规划适用于子问题不是独立的情况,也就是各子问题包含公共的子子问题.动态规划算法对每个子子问题只求解一次,将其结果保存在一张表中,从而避免每次遇到各个子问题时重新计算答案. 动态规划通常应用于最优化问题.此类

动态规划 - 最长公共子序列(LCS)

最长公共子序列也是动态规划中的一个经典问题. 有两个字符串 S1 和 S2,求一个最长公共子串,即求字符串 S3,它同时为 S1 和 S2 的子串,且要求它的长度最长,并确定这个长度.这个问题被我们称为 最长公共子序列问题. 与求最长递增子序列一样,我们首先将原问题分割成一些子问题,我们用 dp[i][j]表示 S1 中前 i 个字符与 S2 中前 j 个字符分别组成的两个前缀字符串的最 长公共子串长度. 显然的,当 i. j 较小时我们可以直接得出答案,如 dp[0][j]必 等于 0.那么,

LeetCode -- Triangle 路径求最小和( 动态规划问题)

人们常说"细节决定成败". 编码工作中,同样需要关注细节. 本文将给出3个小实例来说明编码中关注细节的重要性,同时给出作者对如何注意编码细节的一点见解(说的不对,请指正). 例1 这个问题如此地显而易见,竟然没有被发现. List<int> numList = new List<int>(); numList.Add(3); numList.Add(1); numList.Add(4); numList.Add(2); numList.Add(5); numLi

《算法导论》读书笔记之第16章 0-1背包问题—动态规划求解

原文:http://www.cnblogs.com/Anker/archive/2013/05/04/3059070.html 1.前言 前段时间忙着搞毕业论文,看书效率不高,导致博客一个多月没有更新了.前段时间真是有些堕落啊,混日子的感觉,很少不爽.今天开始继续看算法导论.今天继续学习动态规划和贪心算法.首先简单的介绍一下动态规划与贪心算法的各自特点及其区别.然后针对0-1背包问题进行讨论.最后给出一个简单的测试例子,联系动态规划实现0-1背包问题. 2.动态规划与贪心算法 关于动态规划的总结