算法知识点梳理

前言

转眼间,蓝桥杯真的近在咫尺了,正赛即将开始!

小博主作为本次参加蓝桥杯算法大赛的一员,心情也是非常忐忑!

但忐忑归忐忑,还是要静下心来,再梳理一波算法和可能的考点!

希望对大家能有帮助,衷心祝愿友友们都能取得理想的成绩!

一、数据结构

好的数据结构,不仅让题目思路非常明朗,还能增加通过的概率,最重要的有时可以解决掉可恶的运行超时!

1.1 vector

vector数组优点在于无限大,一般不会出现内存溢出的情况,一维二维都很适合。还可以搭配结构体,非常好用。

声明方式:

vector<Type> a;  //一维
vector<Type> b[100];  //声明一个100行,不限列的二维数组,其实超过100行完全可以

添加数据:

#include<bits/stdc++.h>
using namespace std;
vector<int>q[10];
int main()
{
	q[13].push_back(1);
	cout<<q[13][0]<<endl;
	return 0;
}

注意是push_back( )

常用参数:

排序
sort(q.begin(),q.end())  //默认升序排序
bool cmp(int a,int b)
{
    return a>b;
}
sort(q.begin(),q.end(),cmp) //降序排序
删除元素
q.erase(q.begin()+pos);  //删除单个,pos对应的位置为要删除的元素
q.erase(q.begin()+start,q.begin()+end);  //删除start到end之间的元素

删除这个功能其实挺好的,普通数组删完中间如果需要整体前移,显然用vector更好

1.2 栈stack

栈的特点在于先进先出,比如计算表达式的值,可以用栈存储数字和符号,是个不错的选择

声明方式:
stack<Type> q;
常用操作:
q.push();  //入栈操作
q.top();  //读取栈顶元素
q.pop();  //删除栈顶元素
q.size();  //返回栈中元素个数
q.empty();  //检查栈是否为空

1.3 队列queue

队里的特点是先进先出,是BFS算法的核心数据结构

声明方式:
queue<Type> q;
常用操作:
q.push();  //入栈操作
q.front();  //读取队列第一个元素
q.pop();  //删除队首元素
q.back();  //返回队尾元素
q.size();  //返回栈中元素个数
q.empty();  //检查栈是否为空

1.4优先队列priority_queue

默认由大到小排序,可以手动改为由小到大

#include<bits/stdc++.h>
using namespace std;
priority_queue<int, vector<int>, greater<int>> q;  //改为从小到大排序
bool cmp(int a,int b)
{
	return a>b;
}
int main()
{
	q.push(0);
	q.push(1);
	cout<<q.top()<<endl;
	return 0;
}

比如用于求最短路径的迪杰斯塔拉算法

1.5 set集合

set和优先队列类似,也是一个有序的集合,然后区别就在于set能够去重,即不会存在重复的元素。

声明方式:

set<Type> q;

常用操作:

set.insert();  //插入数据
set.erase();  //删除数据
set.empty();  //判断是否为空
set.size();  //返回元素个数
set.find(k);  //返回一个迭代器,指向键值k

1.6map容器

map容器能够实现关联,即把A和B通过key->value的方式关联到一起。

声明方式:

map<type1,type2>m;

常用操作:

m[key]=value;  //直接赋值
for(map<type1,type2>::iterator it=m.begin();it!=m.end();it++) //生成迭代器
{
	cout<<"key"<<it->first<<endl;
	cout<<"value"<<it->value<<endl;
}

1.7自定义结构体struct

这一部分相当灵活了,需要根据自己和题目的需求灵活确定结构体

struct point
{
    int x;
    int y;
    point(int a,int b)
    {
        x=a;
        y=b;
    }
    point(){}
    bool opeartor <(const point&a)const  //这一步是用来重载运算符的操作,目的是为了让优先级队列
    {								  //从小到大输出,这个背一下模版就可以,一般用的概率不大
        retuan x>a.x;  
    }
}

俗话说,好的工具是成功的一半,现在我们已经有了强大的 stl 库和编写好的结构体,是时候真正进入算法部分了

二、算法部分

2.1最大公约数gcd

int gcd(int a,int b)
{
    if(a<b)
    {
        int temp=a;
        a=b;
        b=temp;
    }
    if(b==0)
    	return a;
    else
    	return gcd(b,a%b);
}

最小公倍数=x*y / gcd(x,y)

遇到这类问题,直接上算法模版就完事了!

2.2 快速幂

要快速的求出 a的n次方 是多少,那么就要用到快速幂公式

int fastPow(int a, int n)
{
    if(n==1)
    	return a;
    int temp=fastPow(a,n/2);
    if(n%2==1)
    	return temp*temp*a;
    else
    	return temp*temp;
}

解释一下原理,快速幂就快在采用了类似二分法的思想,比如求一个2的16次方,正常的话用for循环累乘16个2,但是快速幂的方法就是看成先求 2²,然后再求2² * 2²,然后再求 (2² * 2²)*(2² * 2²),然后再求

((2² * 2²)(2² * 2²))((2² * 2²)*(2² * 2²)),总共进行了4次,就快速的达到了我们想要求的结果。

2.3 DFS(深度优先遍历)

应用领域:最短路径问题,以及传播,感染之类的问题。笼统来讲就是由关于某个点可达性的问题。

主要知识点:

1 结构体struct point{} (这个看情况,不一定有)

2 方向数组dir[4][2] ={上,下,左,右}

3 dfs()算法,其实就是朝着某个方向一直走,走不通再回溯

此部分代码由我之前的一篇博客部分粘贴而来,感兴趣的可以看看原文 算法练习题38—蓝桥杯2018省赛“全球变暖”_杨大熊的代码世界的博客-CSDN博客

int dir[4][2]={{0,-1},{1,0},{0,1},{-1,0}};  //代表向左,向下,向右,向上,尽量和数组坐标保持一致
int flag=0;									//不建议用数学中的坐标系,容易乱
struct point
{
	int x;
	int y;	
};
bool check(int x,int y)
{
	if(x>=1&&x<=n&&y>=1&&y<=n)
	{
		return true;
	}
	return false;
}
void dfs(point a)
{
	juzhen[a.x][a.y]='*';  //证明被遍历
	point temp=a;
	for(int i=0;i<4;i++)  //分别为向左,向下,向右,向上
	{
		temp.x=a.x+dir[i][0];
		temp.y=a.y+dir[i][1];
		if(check(temp.x,temp.y)&&juzhen[temp.x][temp.y]=='.')   
		{
			dfs(temp);
		}
		else if(check(temp.x,temp.y)&&juzhen[temp.x][temp.y]=='#')
		{
			juzhen[temp.x][temp.y]='@';  
		}
	}
	return ;
}

2.4 BFS(广度优先遍历)

广搜和深搜解决的问题有很多类似,因此这两种方法都可以使用,但是小博主本身递归掌握的非常不好,因此更习惯用广搜。

主要知识点:

1 结构体struct point{} (这个看情况)

2 方向数组dir[4][2] ={上,下,左,右}

3 queue队列,核心中的核心

3 bfs()算法,将某个点的可达点全部装入queue中,弹出此点,然后再对queue中的下一个元素进行上述操作,直到队列为空。

int dir[4][2]={{-1,0},{0,-1},{1,0},{0,1}};  //依次为向左,向上,向右,向下 
int Wx,Hy,num; 
#define CHECK(x,y)(x<Wx&&x>=0&&y>=0&&y<Hy)
struct node
{
int x;
int y;
};
void BFS(int dx,int dy)
{
	queue<node> q;  //声明一个队列q,BFS算法的核心 
	num=1;
	node start,next;  //开始节点和下一个节点
	start.x=dx;
	start.y=dy;
	q.push(start);
	while(!q.empty())
	{
		start=q.front();
		q.pop();
		for(int i=0;i<4;i++)
		{
			next.x=start.x+dir[i][0];
			next.y=start.y+dir[i][1];
			if(CHECK(next.x,next.y)&&room[next.x][next.y]=='.')
			{
				room[next.x][next.y]='#'; //进队之后标记被处理 
				num++;
				q.push(next); 
			}
		}
	}
} 

2.5 动态规划

动态规划,永远滴神!感觉动归真的是难度最高的一种。

个人感觉动归要写好,首先要把dp数组的结构想清楚,是建立一维的还是二维的,然后dp中每个状态的意义代表什么等等。

一维dp例题:路径

小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图 中的最短路径。

小蓝的图由 2021 个结点组成,依次编号 1 至 2021。

对于两个不同的结点 a, b,如果 a 和 b 的差的绝对值大于 21,则两个结点 之间没有边相连;如果 a 和 b 的差的绝对值小于等于 21,则两个点之间有一条 长度为 a 和 b 的最小公倍数的无向边相连。

例如:结点 1 和结点 23 之间没有边相连;结点 3 和结点 24 之间有一条无 向边,长度为 24;结点 15 和结点 25 之间有一条无向边,长度为 75。

请计算,结点 1 和结点 2021 之间的最短路径长度是多少。

答案:10266837

这个题建立一维的dp就非常合适,因为计算的是结点n到1的最短距离,不需要求n到x(随机指定)的距离,因此一维的就可以实现。

#include<bits/stdc++.h>
using namespace std;
int dp[2030];  //表示从i到1的距离
int gcd(int a,int b)
{
	if(b==0)
	{
		return a;
	}
	else
	{
		return gcd(b,a%b);
	}
}
int main()
{
	for(int i=1;i<=2021;i++)
	{
		for(int j=i+1;j<=i+21;j++)
		{
			if(dp[j]==0)
			{
				dp[j]=dp[i]+i*j/gcd(j,i);
			}
			else
			{
				dp[j]=min(dp[j],dp[i]+i*j/gcd(j,i));
			}
		}
	}
	cout<<dp[2021]<<endl;
	return 0;
}
二维dp例题:砝码称重

你有一架天平和 N 个砝码,这 N 个砝码重量依次是 W1, W2,⋅⋅⋅, WN。

请你计算一共可以称出多少种不同的重量? 注意砝码可以放在天平两边。

输入格式
输入的第一行包含一个整数 N。

第二行包含 N 个整数:W1,W2,W3,⋅⋅⋅,WN。

输出格式
输出一个整数代表答案。

样例输入
3
1 4 6
1
2
样例输出
10
1
样例说明
能称出的 10 种重量是:1、2、3、4、5、6、7、9、10、11。

1=1;

2 = 6 − 4 (天平一边放 6,另一边放 4);

3=4−1;

4=4;

5=6−1;

6=6;

7=1+6;

9=4+6−1;

10=4+6;

11=1+4+6。

这个题建立就要建立二维的dp【i】【j】数组了,表示在当前已有的 i 种砝码下,是否可以称出重量 j 来

AC代码:

#include<bits/stdc++.h>
using namespace std;
int dp[105][100005];
int w[150];
int main()
{
	int n;
	cin>>n;
	int sum=0;
	for(int i=0;i<n;i++)
	{
		cin>>w[i];
		sum+=w[i];
	}
	sort(w,w+n);  //将砝码从小到大排列
	dp[0][w[0]]=1;  //第一个砝码先赋初值
	for(int i=1;i<n;i++)  //遍历砝码的次序
	{
		dp[i][w[i]]=1;   //当前重量一定可以称出,即只放这一个砝码
		for(int j=1;j<=sum;j++)  //对于每一种重量,分别判断
		{
			if(dp[i-1][j]==1)  //之前能凑出来这种重量
			{
				dp[i][j]=1;
				dp[i][abs(j-w[i])]=1;
				dp[i][j+w[i]]=1;
			}
		}
	}
	int ans=0;
	for(int i=1;i<=sum;i++)
	{
		if(dp[n-1][i]==1)
		{
			ans++;
		}
	}
	cout<<ans<<endl;
	return 0;
}

2.6 其他

日期问题,贪心问题,还有一些字符串的操作,方法上可能没有模版,感觉更偏向平时的练习和考场发挥。

先写到这里吧,也非常感谢大家能够看完这些,写的比较仓促,没有太好的排版,见谅见谅。

最后再次祝各位友友们和我自己都能取得一个心目中理想的成绩!

天道酬勤!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杨大熊的代码世界

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值