南京邮电大学算法与设计实验三:动态规划法(最全最新,与题目要求一致)

实验原理:

1、用动态规划法和备忘录方法实现求两序列的最长公共子序列问题。要求掌握动态规划法思想在实际中的应用,分析最长公共子序列的问题特征,选择算法策略并设计具体算法,编程实现两输入序列的比较,并输出它们的最长公共子序列。

2、用动态规划法和备忘录方法求解矩阵相乘问题,求得最优的计算次序以使得矩阵连乘总的数乘次数最少,并输出加括号的最优乘法算式。

实验内容:

1最长公共子序列

(1)最长公共子序列(Longest Common Subsequence, LCS)问题是:给定两个字符序列

X={x1,x2,……,xm} Y={y1,y2,……,yn},要求找出 A  B 的一个最长公共子序列。

例如:X={a,b,c,b,d,a,b}Y={b,d,c,a,b,a}。它们的最长公共子序列 LSC={b,c,b,a}

通过“穷举法”列出 X 的所有子序列,检查其是否为 Y 的子序列并记录最长公共子序列的

长度这种方法,求解时间为指数级的,因此不可取。

2)分析 LCS 问题特征可知,如果 Z={z1,z2,……,zk}为它们的最长公共子序列,则它们一定

具有以下性质:

  1.  xm=yn,则 zk=xm=yn,且 Zk-1  Xm-1  Yn-1 的最长公共子序列;
  2.  xmyn zkxm,则 Z  Xm-1  Y 的最长公共子序列;
  3.  xmyn zkyn,则 Z  X  Yn-1 的最长公共子序列。

这样就将求 X  Y 的最长公共子序列问题,分解为求解较小规模的子问题:

  •  xm=yn,则进一步分解为求解两个(前缀)子字符序列 Xm-1  Yn-1 的最长公共子列问题;
  • 如果 xmyn,则原问题转化为求解两个子问题,即找出 Xm-1  Y 的最长公共子序列与找出 X  Yn-1 的最长公共子序列,取两者中较长者作为 X  Y 的最长公共子序列。由此可见,两个序列的最长公共子序列包含了这两个序列的前缀的最长公共子序列,具有最优子结构性质。

3)令c[i][j]保存字符序列Xi={x1,x2,……,xi}Yj={y1,y2,……,yj}的最长公共子序列的长度。

由上述分析可得如下递推式:

由此可见,最长公共子序列的求解具有重叠子问题性质,如果采用递归算法实现,会得

 

到一个指数时间算法。因此需要采用动态规划法自底向上求解,并保存子问题的解,这样可

以避免重复计算子问题,在多项式时间内完成计算。

4)为了能由最优解值进一步得到最优解(即最长公共子序列),还需要一个二维数组 s[][]

数组中的元素 s[i][j]记录 c[i][j]的值是由三个子问题 c[i-1][j-1]+1,c[i][j-1] c[i[1]1][j]中的哪一个计算得到,从而可以得到最优解的当前解分量(即最长公共子序列中的当

前字符),最终构造出最长公共子序列自身。

5)编程定义 LCS 类,计算最长公共子序列长度,并给出最长公共子序列:

(注意:语言中数组下标由 0 开始,而实际数据在一维数组 a和二维数组 c中的存

放却是从下标为 1 处开始。)

类中数据成员主要有二维数组 c  s 用于动态规划法求解过程中保存子问题的求解结

果,一维数组 a  b 用于存放两个字符序列, n 为两个字符序列中实际字符的个数。这

些数据成员均应在 LCS 类的构造函数中进行初始化:

代码:

#include <iostream>
#include <string>
using namespace std;
int const MaxLen = 50;

class LCS
{

public:
    LCS(int nx, int ny, char* x, char* y)
    {
        m = nx;
        n = ny;
        a = new char[m + 2];
        b = new char[n + 2];
        memset(a, 0, sizeof(a));
        memset(b, 0, sizeof(b));
        for (int i = 0; i < nx + 2; i++)
            a[i + 1] = x[i];
        for (int i = 0; i < ny + 2; i++)
            b[i + 1] = y[i];
        c = new int[MaxLen][MaxLen];
        s = new int[MaxLen][MaxLen];
        memset(c, 0, sizeof(c));
        memset(s, 0, sizeof(s));
    }
    int LCSLength();

    void CLCS()
    {
        CLCS(m, n);
    }

private:
    void CLCS(int i, int j);
    int(*c)[MaxLen], (*s)[MaxLen];
    int m, n;
    char* a, * b;
};

int LCS::LCSLength()
{
    for (int i = 1; i <= m; i++)
        c[i][0] = 0;
    for (int j = 1; j <= n; j++)
        c[0][j] = 0;
    for (int i = 1; i <= m; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            if (a[i] == b[j])
            {
                c[i][j] = c[i - 1][j - 1] + 1;
                s[i][j] = 1;
            }
            else if (c[i - 1][j] >= c[i][j - 1])
            {
                c[i][j] = c[i - 1][j];
                s[i][j] = 2;
            }
            else
            {
                c[i][j] = c[i][j - 1];
                s[i][j] = 3;
            }
        }
    }
    return c[m][n];
}

void LCS::CLCS(int i, int j)
{
    if (i == 0 || j == 0)
        return;
    if (s[i][j] == 1)
    {
        CLCS(i - 1, j - 1);
        cout << a[i];
    }
    else if (s[i][j] == 2)
        CLCS(i - 1, j);
    else
        CLCS(i, j - 1);
}

int main() {
    int nx, ny;
    char *x = new char[MaxLen], *y = new char[MaxLen];
    cout << "请输入X (不含空格)" << endl;
    scanf("%s", x, sizeof(x));
    nx = strlen(x);
    cout << "请输入Y (不含空格)" << endl;
    scanf("%s", y, sizeof(y));
    ny = strlen(y);
    LCS lcs(nx, ny, x, y);
    cout << "X和Y最长公共子序列的长度为:" << lcs.LCSLength() << endl;
    cout << "该序列为" << endl;
    lcs.CLCS();
    cout << endl;
    delete[]x;
    delete[]y;
    return 0;
}

 实验结果:

复杂度分析:

int LCSLength()的平均时间复杂度为O(n)

void CLCS()的平均时间复杂度为O(nlogn)

2、矩阵连乘

1)求解目标

 n 个矩阵{A1,A2,……An}中两个相邻矩阵 Ai Ai+1均是可乘的,Ai的维数为 pi×pi+1

Ai+1 的维数为 pi+1×pi+2。求 n 个矩阵 A1A2……An 连乘时的最优计算次序,以及对应的最少

数乘次数。

两矩阵相乘 AiAi+1  pi×pi+1×pi+2 次数乘,可得 pi×pi+2 的结果矩阵。

而矩阵连乘 AiAi+1……Aj(简记为 A[ij])求得 pi×pj+1 的结果矩阵时,采用不同的计算

次序,对应的总数乘次数也不同。

2)例如:个矩阵连乘 A1A2A3A4,其中 A1 的维数:50×10A2的维数:10×40A3 的维数:

40×30A4的维数:30×5。有 5 种不同的计算次序:

次序 1:(((A1A2A3A4 需要 50×10×40+50×40×30+50×30×5=87500 

次序 2:((A1A2)(A3A4)) 需要 50×10×40+40×30×5+50×40×5=36000 

次序 3:((A1A2A3))A4 需要 10×40×30+50×10×30+50×30×5=34500 

次序 4:(A1((A2A3A4)) 需要 10×40×30+10×30×5+50×10×5=16000 

次序 5:(A1A2A3A4))) 需要 40×30×5+10×40×5+50×10×5=10500 

3)将二维数组 m[i][j]定义为:计算 A[ij]所需的最少数乘次数;

二维数组 s[i][j]定义为:计算 A[ij]的最优计算次序中的断开位置(例如:若计算 A[i

j]的最优次序在 Ak  Ak+1之间断开,ik<j,则 s[i][j]=k)。

4)当 i=j 时,A[ij]=Ai是单一矩阵,无须计算,因此 m[i][j]=0

 i<j 时,m[i][j]=min{m[i][k]+m[k+1][j]+pipk+1pj+1} (ik<j)

5)算法思路

因为计算 m[i][j]时,只用到已计算出的 m[i][k] m[k+1][j]。所以首先计算出 m[i][i]=0

i=1,2,……n;然后再根据递归式,按矩阵链长递增的方式依次计算 m[i][i+1]i=12,…n[1]1(矩阵链长度为 2);m[i][i+2]i=12,…n-2(矩阵链长度为 3);……则 m[1][n]就是问题

的最优解值(最少数乘次数)。

要构造问题的最优解,根据 s 数组可推得矩阵乘法的次序。从 s[1][n]可知计算 A[1n]

的最优加括号方式为(A[1s[1][n]])(A[s[1][n]+1n])。其中 A[1s[1][n]]的最优加括号方式

又为(A[1s[1][s[1][n]]](A[s[1][s[1][n]]+1s[1][n]])。……照此递推下去,最终可以确定

A[1n]的最优完全加括号方式,构造出问题的一个最优解。

6)动态规划法实现的算法提示

(请分别对实例 1A1 维数:50×10A2 维数:10×40A3 维数:40×30A4 维数:

30×和实例 2A1 维数:30×35A2 维数:35×15A3 维数:15×5A4 维数:5×10

A5 维数:10×20A6 维数:20×25 分别求解。)

代码:

#include <iostream>
#include <cstring>
#define MAX 1024
using namespace std;

int n;
int p[MAX];
int m[MAX][MAX],s[MAX][MAX];

int MChain() {
    for(int r=1; r<n; r++) {
        for(int i=0; i<n-r; i++) {
            int j = i+r;
            m[i][j]=m[i][i]+m[i+1][j]+p[i]*p[i+1]*p[j+1];
            s[i][j]=i;
            for(int k=i+1; k<j; k++) {
                int t = m[i][k]+m[k+1][j]+p[i]*p[k+1]*p[j+1];
                if(t<m[i][j]) {
                    m[i][j]=t;
                    cout<<"更新m["<<i<<"]["<<j<<"]的值为:"<<t<<endl;
                    s[i][j]=k;
                    cout<<"更新s["<<i<<"]["<<j<<"]的值为:"<<k<<endl;
                }
            }
            cout<<"最终求出:m["<<i<<"]["<<j<<"]的值为:"<<m[i][j]<<endl;
        }
    }
    return m[0][n-1];
}

void Traceback(int i,int j) {
    if(i==j) {
        cout << 'A' << i;
        return ;
    }
    if(i<s[i][j]) cout << '(';
    Traceback(i,s[i][j]);
    if(i<s[i][j]) cout << ')';
    if(s[i][j]+1<j) cout << '(';
    Traceback(s[i][j]+1,j);
    if(s[i][j]+1<j) cout << ')';
}

int main() {
    cin >> n;
    for(int i=0; i<=n; i++) {
        cin>>p[i];
    }
    for(int i=0; i<n; i++) {
        m[i][i] = 0;
        s[i][i]=i;
    }
    cout << "最少数乘次数:" <<  MChain() << endl;
    cout << "最优计算次序:" << '(';
    Traceback(0,n-1);
    cout << ')';
    return 0;
}

运算结果 :

实验小结:

在本次实验中,我主要学习了最长公共子序列与矩阵连乘的相关知识:

经过代码的学习,我得出最长公共子序列的计算工作模式,如下图所示

 

关于矩阵连乘我制作了一个矩阵连乘的示意图:

···图损坏了。。。。

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/22186.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

编译原理之词法分析实验(附完整C/C++代码与总结)

一、实验内容 通过完成词法分析程序&#xff0c;了解词法分析的过程。编制一个读单词程序&#xff0c;对PL/0语言进行词法分析&#xff0c;把输入的字符串形式的源程序分割成一个个单词符号&#xff0c;即基本保留字、标识符、常数、运算符、分界符五大类。 对PL/0语言进行词法…

【野火启明_瑞萨RA6M5】按键输入检测

文章目录 一、GPIO输入——按键输入检测二、硬件设计三、软件设计下载验证 一、GPIO输入——按键输入检测 按键检测原理 按键机械触点断开、闭合时&#xff0c;由于触点的弹性作用&#xff0c;按键开关不会马上稳定接通或一下子断开&#xff0c;使用按键时会产生 下图中的带波…

APlayer MetingJS 音乐播放器使用指南

文章目录 1.引用2.安装3.APlayer 原生用法4.MetingJS 的用法 1.引用 APlayer 是一个简洁漂亮、功能强大的 Html5 音乐播放器&#xff0c;GitHub地址&#xff1a;https://github.com/DIYgod/APlayer MetingJS 是为 APlayer 添加网易云、QQ音乐等支持的插件&#xff0c;GitHub地…

MySQL 用户管理

目录 用户管理 用户 用户信息 创建用户 删除用户 修改用户密码 数据库的权限 给用户 注意&#xff1a;如果发现赋权限后&#xff0c;没有生效&#xff0c;执行如下指令&#xff1a; 回收权限 用户管理 如果我们只能使用 root 用户&#xff0c;这样存在安全隐患。这时…

用streamlit,几行代码就可以拥有漂亮图表!

大家注意&#xff1a;因为微信最近又改了推送机制&#xff0c;经常有小伙伴说错过了之前被删的文章&#xff0c;比如前阵子冒着风险写的爬虫&#xff0c;再比如一些限时福利&#xff0c;错过了就是错过了。 所以建议大家加个星标&#xff0c;就能第一时间收到推送。&#x1f44…

QTableWidget样式设置

QTableWidget的样式分为几个部分&#xff1a; 分别是&#xff1a; 外框&#xff1a;QTableWidget 表头&#xff1a;QHeaderView 表头字段&#xff1a;QHeaderView::section 表格&#xff1a;QTableWidget::item 选中的表格&#xff1a;QTableWidget::item::selected 水平滚动条…

JDBC详解(六):数据库事务(超详解)

JDBC详解&#xff08;六&#xff09;&#xff1a;数据库事务&#xff08;超详解&#xff09; 前言一、数据库事务介绍二、JDBC事务处理三、事务的ACID属性1、数据库的并发问题2、四种隔离级别3、在MySql中设置隔离级别 前言 本博主将用CSDN记录软件开发求学之路上亲身所得与所…

海康威视 2024届 数字逻辑设计 实习笔试分析

说明 记录一下 5月11日晚&#xff0c;做的海康威视的一场笔试。分享给需要的IC人。 岗位&#xff1a;数字逻辑设计工程师&#xff08;浙江 杭州&#xff09; 转载需要本人同意&#xff01; 我的见解不一定都是准确的&#xff0c;欢迎评论区交流指正~~ 单选题 1、&#xff…

一分钟带你了解网络安全(如何自学)

一、关于网络安全职业 早些年&#xff0c;网络安全刚起步&#xff0c;作为一个网络安全从业人员&#xff0c;最苦恼的事情就是每当回到村里变成狗蛋儿的时候&#xff0c;七大姑八大姨&#xff0c;邻里乡亲&#xff0c;村子里的各种人都会来找你&#xff0c;狗蛋儿&#xff0c;你…

研报精选230519

目录 【行业230519头豹研究院】2023年中国产后康复设备行业词条报告 【行业230519山西证券】有色金属行业周报&#xff1a;锂价快速回升&#xff0c;释放锂电行业复苏信号 【行业230519头豹研究院】2023年中国氢能重卡行业词条报告 【个股230519西南证券_森麒麟】腾飞的高端轮胎…

漏扫工具-xray 1.9.10(文末附下载)

一、工具介绍 一款功能强大的安全评估工具 二、使用说明 1.使用基础爬虫爬取并对爬虫爬取的链接进行漏洞扫描 xray webscan --basic-crawler http://example.com --html-output vuln.html 2.使用 HTTP 代理进行被动扫描 xray webscan --listen 127.0.0.1:7777 --html-outp…

【sentinel】Sentinel工作主流程以流控规则源码分析

Sentinel工作主流程 在Sentinel里面&#xff0c;所有的资源都对应一个资源名称&#xff08;resourceName&#xff09;&#xff0c;每次资源调用都会创建一个Entry对象。Entry可以通过对主流框架的适配自动创建&#xff0c;也可以通过注解的方式或调用SphU API显式创建。Entry创…

前端026_菜单模块_新增功能

菜单模块_新增功能 1、需求分析2、新增组件实现3、列表引用新增组件4、关闭弹出窗口5、校验表单数据6、提交表单数据6.1、Mock 添加新增模拟接口6.2、Api 调用接口6.3、测试新增功能1、需求分析 菜单管理中有两处有 新增 按钮: 条件区域的是新增一级菜单,传递的参数是0。列表…

Compose 二三事:绘制原理

setContent做了什么 我们基于一个最简单的例子进行分析 class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {Text(text "Hello World!")}} }这里setContent做了什么…

NetApp FAS 混合闪存阵列协助您建构简单易用、聪明智慧、值得信赖的储存基础架构

NetApp FAS 混合闪存阵列 主要优势 1、简单易用&#xff1a;节省您宝贵的时间、金钱和人力 •几分钟内完成储存资源配置。 •以获证实的效率技术降低成本。 •可在单一系统上管理档案与区块资料。 2、聪明智慧&#xff1a;灵活因应瞬息万变的业务需求 •以不中断营运的方式扩…

java(springboot+ssm)/python/php/nodejs/基于vue的景区门票预约管理系统

后端&#xff1a;java(springbootssm)/python/php/nodejs/ 开发运行&#xff1a;微信开发者/hbuilderx 后端:idea/eclipse/vscode/pycharm 模块划分&#xff1a;公告类型、公告信息、用户信息、用户咨询、地区信息、景区信息、景区开放、景区预约、统计信息 本技术是Java平台的…

企企通“码上顺”清洗工具 | 让数据更有价值,让业务更出色

数据清理工作是企业数据管理、数据治理中的最基础的工作之一&#xff0c;不仅是一项苦活、累活&#xff0c;也是一个既考验业务又检验技术的活。 物料主数据作为企业核心的数据资产&#xff0c;在智慧供应链、业财一体化等数字化建设中发挥着重要作用。在当今高速发展的商业环…

2023新版Spring6全新讲解-HelloSpring入门案例

Spring的入门案例 Spring6.0要求的JDK最低版本是17 我们在本课程中使用的版本是5.x版本。这个Spring5的JDK的最低要求是8 一、环境要求 JDK&#xff1a;8 Maven&#xff1a;3.6 Spring:5.3.27 开发工具&#xff1a;IDEA 2021.1.1 二、项目创建 1. 构建项目 在idea中&…

GEE:GEDI数据提取值到矢量区域和点

作者:CSDN @ _养乐多_ 本文将介绍GEDI数据集从GEE上下载到本地,并将每一个激光点的值提取为一个矢量区域,并提取值到矢量区域的方法。 文章目录 一、GEDI数据下载二、GEDI数据栅格矢量化三、提取值到区域四、提取栅格值到点五、空间插值一、GEDI数据下载 GEDI数据下载链接:…

opencv相机标定

当你把摄像机放在一个特定的位置&#xff0c;在它的后面放一个目标图像&#xff0c;或者是把摄像机放到某个物体上&#xff0c;摄像机周围的物体是什么形状&#xff0c;你需要知道这些信息。 当你在计算机上处理图像时&#xff0c;会使用以下三个参数&#xff1a; 1.像素坐标&a…