算法第二、三章上机实践报告

算法第二章上机实践报告

两个有序序列的中位数(分治)

时间复杂度为log(n),首先基本思想因为两个数组已经排好,而且求中位数,所以比较两个数组的中间元素,要是相等,则因为都是中位数,这两个数一定挨在一起,则整体的中位数必定是这个数,所以直接返回这个数。具体看下图

所以基本思路就是这样。为了保证递归的时候两个子数组长度相等,对mid可以这样处理

mid1=(la+ua)/2;
mid2=(lb+ub+1)/2;

这样的意思是若出现一个偶数组一个奇数组,得到的中间位置一样。

因为保证两数组长度一样,会出现数组只有两个的情况,如果按上面的方法会出现

造成死循环。所以进行处理

mid1=(la+ua)/2;
mid2=(lb+ub)/2;

这样处理的都是0位置元素

递归终止条件

当两个数组都只有一个元素的时候,中位数肯定就是小的那一个。

经过上述两个元素数组的处理,会出现一种类型的情况,a数组有1个元素,b数组有2个元素,意味着a的元素大于b的第一个元素,按中位数的定义,比较b的第二个元素和a,小的那个就是中位数。同理a,b调换。

完整代码如下:

#include<bits/stdc++.h>
using namespace std;
int a[100005];
int b[100005];
int binary(int la,int ua,int lb,int ub){
	int mid1=(la+ua)/2,mid2=(lb+ub+1)/2;
	if(la==ua&&lb!=ub){
		if(a[la]>b[ub]){
			return b[ub];
		}
		return a[la];
	}
	if(la!=ua&&lb==ub){
		if(a[ua]<b[ub]){
			return a[ua];
		}
		return b[ub];
	}	
	if(la==ua&&lb==ub){
		if(a[la]>b[lb]){
			return b[lb];
		}
		return a[la];
	}
	else{
		if(la+1==ua&&lb+1==ub){
			mid1=(la+ua)/2;
			mid2=(lb+ub)/2;
		} 
		if(a[mid1]==b[mid2]){
			return a[mid1];
		}
		if(a[mid1]<b[mid2]){
			return binary(mid1,ua,lb,mid2);
		}
		if(a[mid1]>b[mid2]){
			return binary(la,mid1,mid2,ub);
		}
	}
}
int main(){
	int n;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	for(int i=0;i<n;i++){
		cin>>b[i];
	}
	cout<<binary(0,n-1,0,n-1);
}

算法第三章上机实践报告

编辑距离问题

设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字符串进行操作,先创建一个n行m列的dp[i][j],表示a[1..i]变成b[1...j]的最小编辑距离

先考虑删除元素的情况:

观察到如果选择删除操作,a[1..i]变成b[1..j]就只与a[1..i-1]变成b[1..j]有关,即dp[i][j]=dp[i-1][j]+1

插入元素的情况:

观察到如果选择插入操作,a的最后一项与b[j]相等,即不用管b[j],a[1..i]变成b[1..j]就只与a[1..i]变成b[1...j-1]有关,即dp[i][j]=dp[i][j-1]+1

替换元素的情况:

如果选择替换操作,分为b[j]与a[i]是否相等的两种情况,如果b[j]==a[i],则不用做替换,那么dp[i][j]=dp[i-1][j-1],如果不相等,则dp[i][j]=dp[i-1][j-1]+1

因此从上述三种操作中选择最小的一个,便是最小编辑距离。

#include<bits/stdc++.h>
using namespace std;
int dp[2005][2005];
/*
dp[i][j],i表示a从1到i的子串,j表示b从1到j的子串,
dp[i][j]表示a子串变到b子串的最小编辑距离 
*/ 
string a;
string b;
int main(){
	cin>>a;
	cin>>b;
	int n=a.length();
	int m=b.length();
	/*i=0,表示要变的a字符串为空,需要一个一个插入,所以初始化
	条件是如下*/ 
	for(int j=0;j<=m;j++){
		dp[0][j]=j;
	}
	/*j=0,表示b字符串为空,需要把a一个一个删除,所以初始化
	条件是如下*/ 
	for(int i=0;i<=n;i++){
		dp[i][0]=i;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			int tmp=min(dp[i-1][j]+1,dp[i][j-1]+1);
			if(a[i-1]==b[j-1]){
				dp[i][j]=min(tmp,dp[i-1][j-1]);
			}else{
				dp[i][j]=min(tmp,dp[i-1][j-1]+1);
			}
		}
	}
	cout<<dp[n][m];
	return 0;
}

注意初始化操作,不同于以往的一串零。另外,程序里字符串是从0开始计数,因此判断两个字符是否相等时减1。

至于回溯的步骤:

新建一个n行m列的二维数组记录上一步的操作。

时间复杂度和空间复杂度均为n^2。

关于这道题的递推方程,感觉还是要靠某些经验。另外还很难验证自己所想到的递推式的正确性

理解与体会

动态规划最难的是寻找最优子结构和找出递推方程。路漫漫其修远兮,吾将上下而求索

posted @ 2021-10-21 19:42  risotoo  阅读(48)  评论(0编辑  收藏  举报