目录
我也从没想过枚举、模拟和递归还有这么多的花样。总而言之,如果你觉得这篇文章还不错,劳烦多多支持一下!码字不易,感谢你的观看!
前言
这个栏目是对我算法学习过程的同步记录,我也希望能够通过这个专栏加深自己对编程的理解以及帮助到更多像我一样想从零学习算法并参加竞赛的同学。在这个专栏的文章中我会结合在编程过程中遇到的各种问题并提出相应的解决方案。当然,如果屏幕前的你有更好的想法或者发现的错误也欢迎交流和指出!不喜勿喷!不喜勿喷!不喜勿喷!那么事不宜迟,我们马上开始吧!
一、枚举
1.基本介绍
枚举算法是一种基础算法,属于暴力破解的一种。其本质就是枚举出所有可能的情况,再根据题目所给的限制条件来得到符合题意的情况。注意,枚举算法所应用的情景一般是规模较小的且解空间元素可穷举。暴力破解直观简单,但是需要注意的是其在处理过程一般基本不会存在优化,因而时间复杂度会偏高,有可能导致我们的题目样例没法全部通过。但是没关系,我们不需要将每个题都做全对,必要的时候拿到部分分来为后面的题节省时间也是成功的应试策略。
2.代码示例
此处的例题是lanqiao OJ 3227。题目链接如下:https://www.lanqiao.cn/problems/3227/learning/
代码示例如下:
#include<bits/stdc++.h>
using namespace std;
map<int,int>mp;
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n,m;cin>>n>>m;
for(int i=0;i<=n;++i)
{
int x;cin>>x;
mp[x]++;
}
for(const auto &[x,y] : mp)
{
if(2*y>m*n)cout<<x<<"\n";
}
return 0;
}
二、模拟
1.基本介绍
模拟算法通过用程序模拟实际情况来解决问题,一般容易理解但是实现起来比较复杂,会有许多需要注意的东西。模拟题一般不会有很难的算法,因为其是由较多简单但是不好处理的部分组成的,重点考察选手的细心程度和整体的逻辑思维。
2.代码示例
这里的例题是lanqiao OJ 551。题目链接如下:https://www.lanqiao.cn/problems/551/learning/
这里我简要介绍一下此类题型的破解思路。这类模拟题一般都设计一个现实中的具体结构的状态变化。而我们所要做的便是根据题目要求选择合适的数据结构(数组、队列、栈等)进行模拟,并且进行进行对应状态的更新。代码示例如下:
#include<bits/stdc++.h>
using namespace std;
const int N=120;
bool a[N][N],b[N][N];
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n,m;cin>>n>>m;
int t;cin>>t;
for(int i=1;i<=t;++i)
{
int x,y;cin>>x>>y;
a[x][y]=1;
}
int k;cin>>k;
while(k--)
{
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
if(a[i][j])b[i][j]=b[i-1][j]=b[i+1][j]=b[i][j+1]=b[i][j-1]=1;
}
//将b复制回a
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)a[i][j]=b[i][j];
}
int ans=0;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
if(a[i][j])ans++;
cout<<ans<<'\n';
return 0;
}
三、递归
1.基本介绍
递归是指函数直接或间接调用自身的过程。解释递归需要如下两个关键要素:
(1)基本情况(递归终止条件):递归函数中的一个条件,当满足该条件时,递归终止,避免无限递归
(2)递归表达式(递归调用):递归函数中的语句,用于解决规模更小的子问题,再将子问题的答案合并成为当前的问题的答案。
2.递归的实现
这里强调一下设计算法时需要注意的细节:
(1)确保递归一定能到递归出口,避免无限递归导致的TLE(超时)、MLE(超内存)或RE(运行错误)。
(2)考虑边界条件,有时递归出口不止一个。
(3)避免不必要的重复计算,例如使用记忆化等手段优化递归函数的性能。
3.递归和循环的比较
(一)递归的特点:
1.直观,简洁,易于理解和实现。
2.适用于问题的规模可以通过递归调用不断减小的情况。
3.可以处理复杂的数据结构和算法,如图和树的遍历。
4.存在栈溢出风险,递归层数不宜过深。
(二)循环的特点
1.直接控制流程,效率高
2.适用于问题规模没有明显衰减,或者需要特定迭代次数的情况
3.适合处理大部分的动态规划问题。
部分情况下,递归和循环可以相互转化。
4.代码示例
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N =1e5+9;
const int p=1e9+7;
//斐波那契数列,结果对1e9+7取模
//带备忘录的递归
ll dp[N];
ll fib(int n)
{
if(dp[n])return dp[n];
if(n<=2)return 1;
return dp[n]=(fib(n-1)+fib(n-2))%p;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;cin>>n;
for(int i=0;i<n;++i)cout<<fib(i)<<"\n";
return 0;
}