目录:
一,搜索
一问:什么是搜索,搜索的本质是什么?
暴力将每一种答案都搜索一遍,找出符合条件的输出(在我看来就这样)
深搜:(DFS)
1.不管有多少路,都先一条路走到黑,不行再返回上一条路,再选择另一条路。
2.dfs的重点在于回溯(用VIS数组进行标记)
3.大部分情况下用递归的方式进行实现。
应用场景:
1.只要我能够找到这个答案就行(暴力的一种思想),我不管,我就有这个东西就行,不管在哪。
2.迷宫问题中:我是否有路径能够走出迷宫。
基础的点:
两道例题:
解释一下我的思想:(我题目的分析应该都比较明确)
注意点:
1.回溯上,因为这边累加的和进行回溯操作。
2.单独设置一个变量进行记录访问到第几个数了。
最经典的就是八皇后问题,在这里我就不细说了。
注意:
dfs并不适合搜寻最短路径问题,因为每一种情况它都需要遍历一遍,而运用BFS就可以直接return掉很多条路径,时间复杂度就会降低很多。(从而不会被超时)。
搜索(剪枝):
基本概念:
树的分支给砍掉。
简单介绍:
1.优化搜索顺序,先去搜索那些分支短的结点。
2.等效的消除:如果分支相等的话就没必要再次进行搜索了。
3.可行性剪枝:发现分支无法满足递归条件,掉进了死胡同了,就可以直接剪掉了。
4.最优性剪枝:求最短路径,求最小值,寻找最优解的时候应用,如果当前状态就已经不满足最优条件了,那之后无论什么状况都不能满足了,就直接剪掉。
例题:(最优性剪枝)
P1433 吃奶酪 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include <iostream>
#include <cmath>
using namespace std;
int n;
bool vis[20];
double a[20], b[20];
double ans = 2222222222.0;
double suanshi(int x, int y)
{
return sqrt((a[x] - a[y]) * (a[x] - a[y]) + (b[x] - b[y]) * (b[x] - b[y]));
}
void dfs(int p, double len, int m)//p
{
if (len > ans) return;//最优性剪枝,我现在的分支就已经不满足条件了,我继续往下搜索没有任何意义
if (m == n) {//递归到点数与题目相同时
ans = len;//让ans取得最小值
return;
}
for (int i = 1; i <= n; i++)
{
if (vis[i]==false) {
vis[i] = true;
dfs(i, len + suanshi(p, i), m + 1);
vis[i] = false;
}
}
}
int main()
{
ios::sync_with_stdio(false);//加快速度
cin>>n;
a[0] = 0;b[0] = 0;
for (int i = 1; i <= n; i++)
cin >> a[i] >> b[i];
dfs(0, 0.0, 0);
printf("%.2lf", ans);
return 0;
}
例题2:(可行性剪枝)
POJ 1419-Graph Coloring_钟一淼的博客-CSDN博客
广搜:(BFS)
应用场景:
1.迷宫问题(求最短路径)(图的最短路径)
2.图中的搜索问题(孤岛)。
模板:
我个人觉得用马的遍历这个模板就很经典
注意:
1.BFS运用队列,多数需要定义结构体。
2.判断范围条件写好 ,一定得保证它在这一范围区域内进行搜索。
例题1:
寻求最短路径(也可以用Floyd算法求解)
P1135 奇怪的电梯(淼淼总结BFS)_钟一淼的博客-CSDN博客
例题2:
关于孤岛的问题:(面积扩张)典型的图中搜索指定量的做法
P1596 [USACO10OCT]Lake Counting S(DFS)_钟一淼的博客-CSDN博客
还是不停的搜索其他的相同的点,搜过的点标记出来即可。(这里直接将搜索到的水坑直接变成旱地即可)。
二,蓝桥杯的总结:
这周接触了蓝桥杯上面的几个题目,分别说一下学到了什么?
例1:(欧拉定理)
平面的增加=交点分割线段的个数。
注意点:
1.这里用了STL库里的函数set做到了有效去重,减少操作次数。
2.还有就是这里的结构体可以直接用STL里的set<pair<int,int>>来定义会更加方便简洁。
例2:(动态规划入门)
数字三角形(基础篇):
题目分析:就是从下到上不停的更新加上左下值或者加上右下值,谁更大,更优!
状态转移方程:
代码如下:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int mp[1010][1010];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) {
cin >> mp[i][j];
}
}
for (int i = n - 1; i > 0; i--) {
for (int j = 1; j <= i; j++) {
mp[i][j] += max(mp[i + 1][j], mp[i + 1][j + 1]);//更新最大值
}
}
cout << mp[1][1] << endl;
}
2.进阶版本:
题目分析:
1.如果说上一个从下面开始向上递归比较简单的话,那这个题目就是向下搜会比较简单。
2.因为这个题必须保证左右方向个数相等,所以就有了图中的两种判别情况。
代码如下:
#include <iostream>
#include <algorithm>
using namespace std;
int mp[1010][1010];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) {
cin >> mp[i][j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j<=i; j++) {
mp[i][j]+=max(mp[i - 1][j], mp[i - 1][j - 1]);
}
}
if (n % 2 == 0) {
cout << max(mp[n][n / 2], mp[n][n / 2 + 1]) << endl;
}
else {
cout << mp[n][n / 2+1] << endl;
}
}
动态规划的问题是非常复杂的,介绍的这种也是基础中的基础,持续更新...
三,关于Codefores比赛的总结
比赛总结:
Codefores CodeTON Round 1 (Div. 1 + Div. 2)比赛总结_钟一淼的博客-CSDN博客
基本需要注意的点都已经写好了,还是算法的优化和数学思维的掌握吧!
关于STL如何降低复杂度?
Codefores CodeTON Round 1 B题_钟一淼的博客-CSDN博客
这道题目其实我分析的还是比较透彻的,主要还是我用了两层for循环导致我直接超时,所以说利用了map映射map<int,bool>mp,直接用键值就能返回bool类型判断该数到底存不存在 。
还可以利用set集合(set<int>v)直接去掉重复值,利用set函数中find()函数,如果存在,就返回该数的下标,如果不存在,就返回最后一个元素迭代器的位置,所以我们这时候的判断条件只需要写if(v.find(value)!=v.end()),则证明该元素存在。
实现代码:
#include <iostream>
#include <set>
using namespace std;
int t;
int n, k;
long long a[260000];
set<int>s;
int main()
{
cin >> t;
while (t--) {
s.clear();
int flag = 0;
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> a[i];
s.insert(a[i]);
}
for (int i = 1; i <= n; i++) {
if (s.find(a[i] + k) != s.end()) {
flag = 1;
break;
}
}
if (flag != 0) {
cout << "YES" << endl;
}
else {
cout << "NO" << endl;
}
}
}
也可以用vector进行优化:(算是STL的一个广泛应用)
实现代码:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
int n, m, a;
cin >> n >> a;
vector<int> v(n);
for (int i = 0; i < n; i++) {
cin >> m;
v.push_back(m);
}
bool ans = false;
if (n == 1) ans = (v[0] == a);
else {
sort(v.begin(), v.end());
int i = 0;
int j = 1;
while (j < n and i < n) {
if (v[i] + abs(a) == v[j]) {
ans = true;
break;
}
else if (v[i] + abs(a) < v[j]) ++i;
else ++j;
}
}
if (ans == true) {
cout << "YSE" << endl;
}
else {
cout << "NO"<<endl;
}
}
}
贪心思想:
总结的一个小题:
Codefores教育代码第125轮C题_钟一淼的博客-CSDN博客
四,周总结
这周真没学什么很系统的东西,还是主要复习了复习搜索的知识点,练了一些题目,虽然阅读的东西很多,但我总觉得我没有吸收进去,所以这周才更加仔细的推敲某些题目,更加深入的了解,零零碎碎的一些东西就全当是积累了,毕竟数学的内容挺多的,还是需要定时积累的,还有关于codefores比赛的一些问题,数学思维有了,我该如何去实现代码,用最优解,因为codefores比赛中的数据较大,基本到了O(n)以上的复杂度都会被卡,还有就是静下心来多看些资料,把之前的题目总结出来,不断优化算法的时间复杂度,下周更新最小生成树的一些内容,道阻且长。