算法第三章上机实践报告
Posted 寥寥晨曦
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法第三章上机实践报告相关的知识,希望对你有一定的参考价值。
算法第三章上机实践报告
1. 实践报告任选一题进行分析。内容包括:
1.1 问题描述
7-4 编辑距离问题 (25 分)
设A和B是2个字符串。 要用最少的字符操作将字符串A转换为字符串B。 这里所说的字符操作包括 (1)删除一个字符; (2)插入一个字符; (3)将一个字符改为另一个字符。 将字符串A变换为字符串B所用的最少字符操作数称为字符串A到 B的编辑距离,记为d(A,B)。 对于给定的字符串A和字符串B,计算其编辑距离 d(A,B)。
输入格式:
第一行是字符串A,文件的第二行是字符串B。
提示:字符串长度不超过2000个字符。
输出格式:
输出编辑距离d(A,B)
输入样例:
在这里给出一组输入。例如:
fxpimu
xwrs
结尾无空行
输出样例:
在这里给出相应的输出。例如:
5
结尾无空行
(本题主要意思是有增删改三种只针对一个字符的操作,输出由字符串A转变到字符串B需要的最小的总操作次数)
1.2 算法描述
本题主要意思是有增删改三种只针对一个字符的操作,输出由字符串A转变到字符串B需要的最小的总操作次数。由题意可得,字符串A到字符串B的最小总操作次数可以由字符A中的前n-1个字符转变到字符串B的前m-1个字符的最小总操作次数决定,以此类推,我们可以发现编辑距离问题最优解包含着其子问题的最优解,所以该问题可用动态规划算法求解。
用填表法进行分析:
a[i][j]表示的意思是A字符串中前i个字符转变成B字符串中的前j个字符所需要的最小操作次数。
当A为空字符时,a[0][j]=j;
当B为空字符时,a[i][0]=i;
所以把空字符加入表格中的行和列,得到一个初始化的表格,再进行表格填写。
|
0(空) |
1(x) |
2(w) |
3(r) |
4(s) |
0(空) |
0 |
1 |
2 |
3 |
4 |
1(f) |
1 |
|
|
|
|
2(x) |
2 |
|
|
|
|
3(p) |
3 |
|
|
|
|
4(i) |
4 |
|
|
|
|
5(m) |
5 |
|
|
|
|
6(u) |
6 |
|
|
|
|
我们可以发现a[2][2]中填的数依赖于它上边的数a[1][2](a[i-1][j])和它左边的数a[2][1](a[i][j-1])以及左上的数a[1][1](a[i-1][j-1]),取上边的数+1和左边+1(即再做一次追加的操作)和左上+1(即修改最后一个字符)的数中的最小值,a[2][2]=min(a[1][2],a[2][1],a[1][1])+1。a[2][1]中填的数依赖于a[1][0],因为此时字符串最后一个字符相等,所以a[2][1]=a[1][0]。可以发现其他空格也是这种规律,填成下表:
|
0(空) |
1(x) |
2(w) |
3(r) |
4(s) |
0(空) |
0 |
1 |
2 |
3 |
4 |
1(f) |
1 |
1 |
2 |
3 |
4 |
2(x) |
2 |
1 |
2 |
3 |
4 |
3(p) |
3 |
2 |
2 |
3 |
4 |
4(i) |
4 |
3 |
3 |
3 |
4 |
5(m) |
5 |
4 |
4 |
4 |
4 |
6(u) |
6 |
5 |
5 |
5 |
5 |
1.3 问题求解:
1.1.1 根据最优子结构性质,列出递归方程式:
a[i][j]=
0 , i=j=0;
a[i-1][j-1] ,A[i]==B[j]
min(a[i-1][j-1],min(a[i-1][j],a[i][j-1]))+1 ,A[i]!=B[j]
1.1.2 给出填表法中表的维度、填表范围和填表顺序。
涉及到两个字符串之间的转换,要求表的维度是二维;因为每个空格都依赖于其左上、左边、上面的三个空格,所以表格要全填,填表顺序按循环是从左到右从上到下,填到最后的空格a[i][j]就是最终的答案。
1.1.3 分析该算法的时间和空间复杂度
填表的的代码如下:
for(i=0;i<=len1;i++)a[i][0]=i;
for(j=0;j<=len2;j++)a[0][j]=j;
for(i=1;i<=len1;i++)
{
for(j=1;j<=len2;j++)
{
if(s1[i-1]==s2[j-1])a[i][j]=a[i-1][j-1];
else a[i][j]=min(a[i-1][j-1],min(a[i-1][j],a[i][j-1]))+1;
}
}
该算法要用到二重循环,所以时间复杂度为O(n^2),要用到一个二维辅助数组,所以空间复杂度为S(n^2).
1.3 心得体会(对本次实践收获及疑惑进行总结)
通过这次实验,我懂得了不止要会写代码,还要将细节考虑清楚,能讲清楚自己的思路。同时做题的时候要先整理思路,写出递归方程和边界条件来作为代码的依据。
2. 你对动态规划算法的理解和体会
通过这次实践,我对动态规划有了更深的理解,也懂得了动态规划解题的方便。在做题的时候,因为动态规划与前面所学的分治法有点像但是不同点是动态规划是自底向上,分治法是从上到下,没区分清楚导致做题的时候总往分治法那边偏离。多做了几题动态规划后发现,动态规划中问题的最优解与子问题的最优解的依赖关系是不同于分治法的。最后运用填表法做题时要充分结合依赖关系来判断表的维度、填表的范围、填表顺序等,才能更好地做题。
揭露动态规划真面目——算法第三章上机实践报告
算法第三章上机实践报告
一、 实践题目
7-2 最大子段和 (40 分)
给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时,定义子段和为0。
要求算法的时间复杂度为O(n)。
输入格式:
输入有两行:
第一行是n值(1<=n<=10000);
第二行是n个整数。
输出格式:
输出最大子段和。
输入样例:
在这里给出一组输入。例如:
6
-2 11 -4 13 -5 -2
输出样例:
在这里给出相应的输出。例如:
20
二、 问题描述
输入一个数,表示接下来输入的数组元素个数,第二行输入所有元素,求出他们的最大子段和(如果都是负数就输出0)。
相当于,从第一个数开始,往后加数,如果是正数就说明这个子段是“有用的”反之“对最大子段没有贡献”。只要判断每次以最后一个数为结尾和前一个数结尾的大小,取大的,递归赋给一个记录用的变量即可。
三、 算法描述
1、动态规划
for(int i=1;i<=n;i++){
dp[i]=max(a[i],dp[i-1]+a[i]);
maxn=max(maxn,dp[i]);
}
从第一个数字开始,先赋初值0给dp数组和记录用的变量maxn,dp[i]为以第i个数字结尾的最大子段和。每次dp[i]都取数组第i个数和dp[i-1]加上目前指向的这个数中较大的那个(意义在于,如果目前指向的那个数比dp[i-1]加上目前指向的这个数还大,说明之前的子段和是负数,没有贡献不取)。而“maxn=max(maxn,dp[i])”就是筛选掉负数,若为正,取最大的子段和(dp数组储存着分别以每个数为结尾的最大子段和)。
2、判断输出
if(maxn) cout<<maxn<<endl;
else cout<<0<<endl;
若maxn为正数则得到最大子段和输出,否则(maxn为0或负数),数组全为负数或者最大子段和即为0,按要求输出0。
3、完整代码
#include<iostream>
#include<algorithm>
using namespace std;
int n,maxn;
int a[10005],dp[10005];
int main(){
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++){
dp[i]=max(a[i],dp[i-1]+a[i]);
maxn=max(maxn,dp[i]);
}
if(maxn) cout<<maxn<<endl;
else cout<<0<<endl;
return 0;
}
四、 算法时间及空间复杂度分析
for(int i=1;i<=n;i++){
dp[i]=max(a[i],dp[i-1]+a[i]);
maxn=max(maxn,dp[i]);
}
循环递归调用n次,动态规划问题一般时间复杂度为O(n)或者O(n^2),显然在这里是O(n);
解这道题中,用到了dp数组和maxn变量作为辅助,空间复杂度同样为O(n)。
五、 心得体会
抓住问题的关键,理清解题思路,一道看似“很难”的题目其实很“简单”。尤其是动态规划问题,不断动态地自下而上添加新的元素得到结果,最后解决问题。
虽然思路想出来了,有时候还是不知道怎么下手去敲代码,反映了自己做题数量太少,打代码地时间和次数太少,还停留在空想地阶段,这是一个大问题,亟待改正和提高。
队友很强,有时候会比较依赖队友,应该先自己思考后再与队友讨论才能够有所提高。
以上是关于算法第三章上机实践报告的主要内容,如果未能解决你的问题,请参考以下文章