3.20—3.27学习总结

一、二分算法初步学习
简单理解部分(引入性题目)
给出一个已经排序完成的数组,从中寻找一个元素,找到后终结(解析置于代码中)
1.初等实现

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <vector>
#include <map>
#include <queue>
#include <algorithm>
#include <math.h>
#include <cstdio>
using namespace std;
int main() 
{
	int a[10] = { 0,1,2,3,4,5,6,7,8,9 };
	int n=10,target;
	int low, height,middle;
	cin >> target;//输入需要查找的元素
	low = a[0];
	height = a[10];//注意初始化位置,在循环之外
	while(low<=height)
	//当循环到这一步之前,使得low==height==middle
	//终止条件是只剩下这一个数,也就是直到low>hetght表明只剩下一个数,此时也是最大的循环次数
	{
		int middle = (low + height) / 2;
		if (target == middle)
			return 1;
		//后两步骤是二分算法的精髓,不断划分区间从而找到元素
		if (target > middle)low = middle + 1;
		//如果目标元素大于中间值,说明位于中间值与最大值之间,更新最小值范围
		if (target < middle)height = middle - 1;
		//如果目标元素小于中间值,说明位于中间值与最小值之间,更新最大值范围
	}
}

对算法精髓部分进行优化:

if (target > middle)low = *upper_bound(a, a + 10, middle);
		if (target < middle)height = *lower_bound(a, a + n, middle);
		//注意,这里的upper_bound返回的是地址值,用*,意味着取地址,能够返回相应地址所指代的数值

2.查找被查找元素在数组中的位置,输出相应的下标

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <vector>
#include <map>
#include <queue>
#include <algorithm>
#include <math.h>
#include <cstdio>
using namespace std;
int main()
{
	int a[10] = { 0,1,2,3,4,5,6,7,8,9 };
	int n = 10, target;
	int low, height, middle;
	cin >> target;//输入需要查找的元素
	low = 0;
	height = 9;
	while (low <= height)
	{
		int middle = (a[low] +a[height]) / 2;//下标表示
		if (target == middle)
			cout << middle;
		if (target > middle)low = *upper_bound(a, a + 10, middle);
		if (target < middle)height = *lower_bound(a, a + n, middle);
	}

}

拓展:
如果对于乱序的数组,想输出相应位置的下标,则可以用结构体先对相应位置进行标记,之后排序,按照二分算法进行查找

struct array 
{
	int x;
	int id;
	//按照之前的方法进行排序元素,找到后输出相应的id数值
}a[10];

二、数字金字塔(动态规划简单入门)
思路过程(模拟的过程):题目所问的是第一层元素的最大值,一开始我们是不知道如何下手的,因为没有一个明显的思路求最大值,但是,如果我们知道第二层元素的最大值,就可以求解第一层元素的最大值,同理,第二层的最大值如何去求呢?要看第三层的每个元素的最大值,第三层怎么求呢?等等如此,逐层推理下去,到最后一步时,递归到第一层元素的最大值,到达了一个已知的部分,则可以求解,具体实现请看代码

#include<iostream>
using namespace std;
int main()
{
	int i, j;
	int a[10][10];
	int n;
	cin >> n;
	for (i = 1; i <= n; i++)
		for (j = 1; j <= i; j++)cin >> a[i][j];
	for (i = n; i >1; i--)//每一行进行递增,使得最大值逐步向上递增,最终使得获得最大值
		//(for循环中循环条件的设置,是能否继续执行的条件)
	{

		for (j = 1; j < i; j++)//让底层开始比较,对上一层进行加和处理,使上一层获得最大值
			a[i - 1][j] += max(a[i][j], a[i][j + 1]);
		//动态规划递归状态方程(用一个式子简化了if语句的书写)(用max简化了方程的存储内容)
	}
	cout << a[1][1];
}

**这也引出了动态规划的核心思路:**从上到下地思考问题,从下到上地书写代码(拿此题为例:使第一层元素最大,就要使第二层最大,逐层延展,在写代码时,从下到上地书写)
三、最长上升子序列个数(记忆化搜索)
将以每一个元素为结尾的最长上升子序列进行记录,记录数值(提前算出,直接保留,大大降低时间复杂度以及代码复杂度)

#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
	int a[10] = {0}, length[10] = {0};
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)cin >> a[i];
	//记录当前元素为终点的最长值
	for (int i = 1; i <= n; i++) 
	{
		length[i] = 1;
		for (int j = 1; j < i; j++) 
			if (a[j] < a[i])length[i] = max(length[j]+1, length[i]);
		//length[j]表示以当前元素为终点的最大子序列
		//已经在之前动态规划,进行记忆化数据后,不用再次求,时间复杂度大大降低
		//为什么不直接+1呢,因为遇到小的数值重置时,length[i]能够对之前较大的数据进行保留
	}
	sort(length + 1, length + n + 1);
	cout << length[n];

}

四、最长公共子序列
(学习此部分内容参考自:https://blog.csdn.net/hrn1216/article/details/51534607?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161682377516780274181204%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=161682377516780274181204&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_v2~rank_v29-2-51534607.pc_search_result_cache&utm_term=%E6%9C%80%E9%95%BF%E5%85%AC%E5%85%B1%E5%AD%90%E5%BA%8F%E5%88%97%E4%BB%80%E4%B9%88%E6%84%8F%E6%80%9D)
LCS性质特征(求长度用动态规划缩短范围的核心原理):设A=“a0,a1,…,am”,B=“b0,b1,…,bn”,且Z=“z0,z1,…,zk”为它们的最长公共子序列。
如果am=bn,则zk=am=bn,且“z0,z1,…,z(k-1)”是“a0,a1,…,a(m-1)”和“b0,b1,…,b(n-1)”的一个最长公共子序列;
如果am!=bn,则若zk!=am,蕴涵“z0,z1,…,zk”是“a0,a1,…,a(m-1)”和“b0,b1,…,bn”的一个最长公共子序列;
如果am!=bn,则若zk!=bn,蕴涵“z0,z1,…,zk”是“a0,a1,…,am”和“b0,b1,…,b(n-1)”的一个最长公共子序列。
用递归方程表示:
在这里插入图片描述

求最长长度代码:

//最长公共子序列
//思路来源:(性质定理)如果am=bn,则同时去掉后,z(k-1)是最后一个元素
//如果am!=bn,则同时去掉后取最大值即可(一定不是公共部分)
#include<iostream>
using namespace std;
int main() 
{
	int a[10] = { 1,3,4,7 }, b[10] = {3,4,1,5}, dp[10][10] = { 0 };
	for (int i = 0; i < 3; i++) //以a数组元素为起点逐个判定
	{
		for (int j = 0; j < 3; j++) 
		{
			if (a[i] == b[j])dp[i+1][j+1] = dp[i][j]+1;
			else dp[i+1][j+1] = max(dp[i][j+1], dp[i+1][j]);//两种情况合并书写
		}
	}
	cout << dp[3][3];

}

动态规划思想体现:1.在这里将一个大问题缩短成无限个小问题,每一个小问题之间互相关联(求公共子序列最长长度,看看将之缩短会怎样)也就是动态规划核心特点:从大到小
2.在列出递归方程后,从小到大地编写实现程序,逆序编写程序


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值