栈与DF
一.栈
1.定义
一种受限制的线性表,我们可以操作的……只有最后一个元素
我们可以从栈的最后插入一个元素,知道最后面的元素是什么,可以把最
后面的元素删了,其它的都做不了
元素先进后出,像极了第一个进入了电梯被挤得下不去的你
我们可以操作的这一头,被称为栈顶
2.两种写法
栈可以分为手写栈和STL栈
手写
const int maxn=100010;
int/char sta[maxn]; //stack是特殊定义,不能做数组名,其实就是STL的栈
int top=0;
..............
stack[++top]=s[i];
手写栈要注意栈顶的数值
STL
stack <int/char> sta;//这里不用开大小
个人理解STL栈是更为方便的,较多的函数简洁明了,就是检查打表时有些麻烦
3.STL特殊函数
4.例题
<1>模拟栈
实现一个栈,栈初始为空,支持四种操作:
(1) “push x” – 向栈顶插入一个数x;
(2) “pop” – 从栈顶弹出一个数;
(3) “empty” – 判断栈是否为空;
(4) “query” – 查询栈顶元素。
现在要对栈进行M个操作,其中的每个操作3和操作4都要输出相应的结果。
输入格式 第一行包含整数M,表示操作次数。
接下来M行,每行包含一个操作命令,操作命令为”push x”,”pop”,”empty”,”query”中的一种。
输出格式 对于每个”empty”和”query”操作都要输出一个查询结果,每个结果占一行。
其中,”empty”操作的查询结果为“YES”或“NO”,”query”操作的查询结果为一个整数,表示栈顶元素的值。
数据范围 1≤M≤100000, 1≤x≤109 所有操作保证合法。
1)思路
比较简单,使用STL栈的内置函数即可
2)代码实现
电脑重启保护,文件没了....
<2>括号匹配加强版
思路
比较繁琐,先是简易思路,就像括号匹配一样,是左括号就入栈
右括号检测top的右括号是否配对,配对栈顶就减一,出栈
但是注意输出的内容,分析一下是最长的配对括号
这时候我们就开一个标记数组,值全部定为false
当出栈时,将两个括号在标记数组中的值改为true
最后遍历标记数组,寻找最长的true值序列即可
有一些细节
在记录标记数组时,显然是左右括号的位置
右括号下标好求
但左括号却没有那么简单
因为栈顶的下标一直是在变化的
可能不是最开始输入的下标
所以我们开一个结构体
分别记录括号的值与输入时的下标
最后即可标记
判断时开一个maxx
每次比较maxx与标记数组连续子序列为true的长度
输出maxx即可
代码实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000010;
struct QA
{
int id;
char kh;
// bool flag=false;
}sta[maxn];
int maxx=-maxn;
int t;
int sum=0;
bool f[maxn];
string s;
int top=0;
int main()
{
// freopen("LongestRegularBracketsSequence.in","r",stdin);
// freopen("LongestRegularBracketsSequence.out","w",stdout);
cin>>s;
for(int i=0;i<s.size();i++)
{
if( s[i]=='(' || s[i]=='[' )
{
top++;
sta[top].kh=s[i];
sta[top].id=i;
}
else if((s[i]==')' && sta[top].kh=='(') || (s[i]==']' && sta[top].kh=='['))
{
// sta[s[i]].flag=true;
f[i]=true;
// sta[sta[top].id].flag=true;
f[sta[top].id]=true;
top--;
}
else top=0;
}
for(int i=0;i<s.size();i++)
{
if(/*sta[i].flag*/ f[i] /*&& sta[i+1].flag*/)
{
sum++;
}
if(!f[i] /*&& (!sta[i+1].flag)*/)
{
if(maxx<sum)
{
maxx=sum;
t=i;
}
sum=0;
}
}
/* for(int i=t-maxx;i<=t-1;i++)
{
cout<<s[i];
}
*/
cout<<t;
return 0;
}
二.搜索之DFS
1.定义
搜索是已知初始状态和目标状态,对问题的中间状态进行枚举和遍历的一种解题算法,所以说搜索是一种变化的和复杂的枚举。
搜索从本质上来说是枚举,但搜索更灵活,更加能够体现智能逻辑的魅力。基于枚举思想的搜索相对单纯的枚举有许多不可替代的优势。
2.如图
3.两种写法
DFS可以用递归实现,也可以用栈实现
DFS不一定在图中,有很多DFS暴力递归搜索解决问题
经常用来解决无法确定该写几层循环的题
递归
栈
利用栈的先进后出的特点实现的,可以采用
递归的方法来完成。
4.剪枝
深度优先搜索,是从初始状态起,利用一定的规则生成搜索树,寻找下一层任一个结点,检查是否出现目标状态,若未出现,以此状态利用规则生成再下一层任一个结点,再检查,重复过程一直到叶节点(即不能再生成新状态节点),当它仍不是目标状态时,回溯到上一层结果,取另一可能扩展搜索的分支。采用相同办法一直进行下去,直到找到目标状态为止。
剪枝,就是减小搜索树规模、尽早排除搜索树中不必要的分支的一种手段。形象地看,就好像剪掉了搜索树的枝条,故称之为“剪枝”。在深度优先搜索中,有以下几类常见的
最优性剪枝
1)定义
对于求最优解的一类问题,通常可以用最优性剪枝,比如在求迷宫最短路的时候,如果发现当前的步数已经超过了当前最优解,那从当前状态开始的搜索都是多余的,因为这样搜索下去永远都搜不到更优的解。通过这样的剪枝,可以省去大量冗余的计算。此外,在搜索可行解的过程中,一旦找到一组可行解,后面所有的搜索都不必再进行了,这算是最优性剪枝的一个特例。
2)方式&例题
重复性剪枝
1)定义
对于某一些特定的搜索方式,一个方案可能会被搜索很多次,这样是没必要的
减去重复的情况
2)方式&例题
奇偶性剪枝
1)定义
当有规律可寻的dfs时,减去重复的一部分
2)方式&例题
可行性剪枝
1)定义
所谓可行性剪枝,就是把能够想到的它不可能出现的情况给它剪掉,对于每一个数,我们有两种选择:选或不选;在这其中,我们用cnt记录了选择数的个数,如果当选择个数cnt==k时,这时再往后选更多的数是没有意义的。所有我们可以直接减去这个搜索分支:
2)方式&例题
5.例题
见上即可
总结
1.栈的使用适合于一些特殊的题目,在先进后出的线性表结构下,不存在栈的下标,但节省了内存,并且一般不会超时
2.暴力搜索可以使我们尽量减少代码运行时间,拿到更多的 分数,但有时候不无脑,考虑剪枝,或者与贪心结合,能让题目A的可能性更大