[运筹学]关于动态规划的2个问题-最长公共子序列与最长非降子序列问题

最近在做搜狗编程的时候,遇到了动态规划一类的问题,感觉很有意思。写此博文总结之。
首先先简单叙述下什么是动态规划。

动态规划

(下面部分概念参考自:百度百科)
把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法。

对于动态规划问题的一些理解(个人看法,可能有误):

大多数动态规划问题都可以用蛮力法或者递归(深度搜索)的思路来解决,但是用动态规划就好了很多;一个很著名的例子,斐波那契数列的求解,用递归当然OK,但是计算下一个数的时候,用到前一个的数的结果,会好很多,我个人认为从概念角度来看,这种计算斐波那契数列的算法(保留上一步的值)也属于动态规划。

下面来介绍2个动态规划的问题。

1.最长非降子序列问题

问题描述:在一个无序的序列 a1 , a2 ,…, am 里,找到一个最长的序列,满足 aiajak ;且 i<j<k
动态规划解题思路:

引入一个状态opt[i]数组,该状态记录的信息是,到该位置,它的最长非将子序列的长度。

下面是我写的C++实现。

#include<iostream>
using namespace std;

int main()
{
    int a[10]={22,12,1,23,1000,-1,98,99,0,4};
    int opt[10]={0};
    opt[1]=1;
    for(int i=1;i<10;i++)
    {
        for(int j=0;j<i;j++)
        {
            if(a[i]>=a[j]&&opt[j]+1>opt[i])
            {
                opt[i]=opt[j]+1;
            }
        }
    }
    int max=0;
    for(int i=0;i<10;i++)
    {
        if(max<opt[i])
            max=opt[i];
    }
    cout<<max<<endl;
    return 0;
}

当然,该问题可以用递归算法求解,用到深度优先搜索算法。有兴趣可实现下,但是明显不如动态规划的办法。

2.最长公共子序列问题

问题描述:对于序列S和T,求它们的最长公共子序列。例如X={A,B,C,B,D,A,B},Y={B,D,C,A,B,A}则它们的lcs是{B,C,B,A}和{B,D,A,B}。

下图来源自网络。是一个很典型的求解。
这里写图片描述

《算法导论》上给出了该问题的求解算法伪代码描述。
这里写图片描述
用C++实现如下。

#include<iostream>
#include<string>
using namespace std;

string a,b;
int num[501][501]={0}; ///记录中间结果的数组
int flag[501][501]={0};    ///标记数组,用于标识下标的走向,构造出公共子序列
void LCS(); ///动态规划求解
void getLCS();    ///采用倒推方式求最长公共子序列

int main()
{
    a="ABCBDAB";
    b="BDCABA";
    LCS();
    cout<<"fuck:"<<num[(int)a.size()][(int)b.size()]<<endl;
    getLCS();
    return 0;
}

void LCS()
{
    int i,j;
    for(i=1;i<=(int)a.size();i++)
    {
        for(j=1;j<=(int)b.size();j++)
        {
            if(a[i-1]==b[j-1])   ///注意这里的下标是i-1与j-1
            {
                num[i][j]=num[i-1][j-1]+1;
                flag[i][j]=1;  ///斜向上标记
            }
            else if(num[i-1][j]>=num[i][j-1])
            {
                num[i][j]=num[i-1][j];
                flag[i][j]=2;  ///向上标记
            }
            else
            {
                num[i][j]=num[i][j-1];
                flag[i][j]=3;  ///向左标记
            }
        }
    }
    cout<<num[(int)a.size()][(int)b.size()]<<endl;
}

void getLCS()
{

    char res[500];
    int i=(int)a.size();
    int j=(int)b.size();
    int k=0;    ///用于保存结果的数组标志位
    while(i>0 && j>0)
    {
        if(flag[i][j]==1)   ///如果是斜向上标记
        {
            res[k]=a[i-1];
            k++;
            i--;
            j--;
        }
        else if(flag[i][j]==2)  ///如果是向右标记
            j--;
        else if(flag[i][j]==3)  ///如果是向下标记
            i--;
    }

    for(i=k-1;i>=0;i--)
        cout<<res[i];
    cout<<endl;
}

本博文参考资料

《算法导论》
博文1:http://www.cnblogs.com/wuyuegb2312/p/3281264.html#q3
博文2:http://www.cnblogs.com/xudong-bupt/archive/2013/03/15/2959039.html
博文3:http://www.cnblogs.com/dartagnan/archive/2011/08/29/2158230.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值