寒假集训——单调队列(滑动窗口)、单调栈
小记:今天又起晚了,睁眼就八点了,真舒服啊。到了教室学长刚开始讲课,我到的时
间刚刚好还能签上到,miao。
今天的内容栈、队列和单调队列单调栈。之前早接触过这些内容,队列了解少一些,但
是单调栈之前理解了好久,理论理解完了又去理解代码,相当的耗时间。虽然再次听之
前的内容也忘了不少,但好歹轻松了一些,不想一开始学的时候呢么吓人了。
今天讲解了几个板子,通过听课和模板代码,对这部分内容了解更多了一些。
OK,接下来该写笔记了。
队列和栈都可以通过数组模拟,但是c++有直接封装好的stl容器,肯定更方便。
\(stack\) 、\(deque\)、\(queue\)分别是栈、双端队列、队列。
特别篇 P1443 马的遍历
其实这是一道bfs的题目但是用到了队列,由于对于bfs队列的过程还是不够熟悉,bfs
学习之后学的队列,使我在这个题中遇到了很大的阻碍(计数),因此特别记录下来
警醒自己,不能只记住板子,代码的流程应该熟悉,不能自以为正确,这个题就是自
以为正确而出现的问题。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 5e6 + 10;
const int M = 10100;
int maxx = -1e18;
int minn = 1e18;
int a[M][M];
int dx[8] = {-2, -2, 2, 2, 1, 1, -1, -1};
int dy[8] = {1, -1, 1, -1, 2, -2, 2, -2};
int n, m, x, y, cnt, nx, ny;
bool f[M][M];
//pair<int, int>r[M];
queue<pair<int, int>> q;
int k = -1;
void bfs()
{
q.push({x, y});
f[x][y] = 1;
while (!q.empty())
{
int i, j;
auto t = q.front();
q.pop();
for (i = 0; i < 8; i++)
{
int xx = t.first + dx[i];
int yy = t.second + dy[i];
if (xx > 0 && yy > 0 && xx <= n && yy <= m && f[xx][yy] == 0)
{
f[xx][yy] = 1;
a[xx][yy] = a[t.first][t.second] + 1;//关键错误点
q.push({xx, yy});
}
}
}
}
signed main()
{
cin >> n >> m >> x >> y;
int i, j;
bfs();
for (i = 1; i <= n; i++)
{
for (j = 1; j <= m; j++)
{
if (a[i][j] == 0)
{
if (i != x || j != y)
printf("%-5d ", k);
else
printf("%-5d ", nx);
}
else
printf("%-5d ", a[i][j]);
}
cout << endl;
}
return 0;
}
P5788 【模板】单调栈
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 5e6 + 100;
const int M = 10100;
int cnt, sum;
int a[N];
stack<int>st; // 定义一个整数栈st
int s[N];
signed main()
{
int n, i; // 定义两个整数变量n和i,n表示数组的长度,i是循环变量
cin >> n; // 从标准输入读取n的值,即数组的长度
for (i = 1; i <= n; i++) // 循环读取数组的元素
{
cin >> a[i]; // 从标准输入读取一个整数并存入数组a的第i个位置
}
for (i = 1; i <= n; i++) // 循环处理数组中的每个元素
{
while (!st.empty() && a[i] > a[st.top()]) // 如果栈不为空且当前元素大于栈顶元素
{
s[st.top()] = i; // 将栈顶元素的插入位置存储到数组s中
st.pop(); // 弹出栈顶元素
}
st.push(i); // 将当前元素的索引i压入栈中
}
for (i = 1; i <= n; i++) // 输出每个元素的插入位置
cout << s[i] << " "; // 在每个位置后面加一个空格并换行
}
P1886 滑动窗口 /【模板】单调队列
#include<bits/stdc++.h> // 包含大部分的C++标准库
using namespace std; // 使用标准命名空间
#define int long long // 定义int为long long类型,增加整数范围
#define endl '\n' // 定义endl为换行符
const int N = 5e6 + 100; // 定义常量N,表示数组的最大长度
const int M = 10100; // 定义常量M,但在这段代码中没有使用
int cnt, sum; // 定义两个全局整数变量,但在这段代码中没有使用
int a[N]; // 定义一个长度为N的整数数组
deque<int> q; // 定义一个整数双端队列q
signed main() { // 主函数,返回类型为signed,通常主函数返回类型为int
int n, k; // 定义两个整数变量n和k,分别表示数组的长度和窗口大小
cin >> n >> k; // 从标准输入读取n和k的值
int i; // 定义一个循环变量i
for (i = 1; i <= n; i++) // 循环读取数组的元素
scanf("%lld", &a[i]); // 从标准输入读取一个长整型数并存入数组a的第i个位置
// 第一部分:升序排序窗口输出顶部元素
for (i = 1; i <= n; i++) {
if (!q.empty() && i - q.front() + 1 > k) // 如果队列不为空且当前位置与队列头部位置之差大于k
q.pop_front(); // 则移除队列头部的元素
while (q.size() && a[i] <= a[q.back()]) // 如果队列非空且当前元素小于等于队列尾部元素
q.pop_back(); // 则移除队列尾部的元素
q.push_back(i); // 将当前位置i加入队列尾部
if (i >= k) // 如果当前位置大于等于k
cout << a[q.front()] << " "; // 则输出队列头部的元素并后面加一个空格
}
cout << endl; // 输出一个换行符
q.clear(); // 清空队列
// 第二部分:降序排序窗口输出顶部元素
for (i = 1; i <= n; i++) {
if (!q.empty() && i - q.front() + 1 > k) // 如果队列不为空且当前位置与队列头部位置之差大于k
q.pop_front(); // 则移除队列头部的元素
while (q.size() && a[i] >= a[q.back()]) // 如果队列非空且当前元素大于等于队列尾部元素
q.pop_back(); // 则移除队列尾部的元素
q.push_back(i); // 将当前位置i加入队列尾部
if (i >= k) // 如果当前位置大于等于k
cout << a[q.front()] << " "; // 则输出队列头部的元素并后面加一个空格
}
cout << endl; // 输出一个换行符
}
P1739 表达式括号匹配
这段代码是一个\(C++\)程序,用于检查给定的字符串是否只包含有效的括号。
首先,程序通过 \(cin >> s\); 从标准输入读取一个字符串 \(s\)。
然后,使用一个循环将字符串 \(s\) 的所有字符(除了最后一个字符)推入一个栈 \(st\)。
之后,程序进入一个循环,该循环将栈中的每个字符逐个弹出并检查:
如果弹出的字符是 $ ) $,则 \(p\) 值增加1,表示已经遇到一个右括号。
如果弹出的字符是 \((\) ,则 \(p\) 值减少1,表示需要找到一个相应的左括号。
如果 \(p\) 的值变为负数,这意味着遇到了一个没有相应左括号的右括号,因此输出
\(NO\) 并结束程序。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 5e6 + 100;
const int M = 10100;
int cnt, sum;
string s;
stack<int> st;
signed main() {
cin >> s;
int i;
for (i = 0; i < s.size() - 1; i++) {
st.push(s[i]);
}
int p = 0;
while (!st.empty()) {
auto t = st.top();
st.pop();
if (t == ')') {
p++;
} else if (t == '(') {
p--;
}
if (p < 0) {
cout << "NO" << endl;
return 0;
}
}
if (p != 0) {
cout << "NO" << endl;
return 0;
}
cout << "YES" << endl;
}
P1440 求m区间内的最小值
滑动窗口
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 5e6 + 100;
const int M = 10100;
int cnt, sum;
deque<int> q;
int a[N];
signed main()
{
int n, m;
int i, j;
cin >> n >> m;
for (i = 1; i <= n; i++)
{
scanf("%lld", &a[i]);
}
// q.push_front(0);
cout << 0 << endl;
for (i = 1; i < n; i++)
{
if (!q.empty() && i - q.front() + 1 > m)
q.pop_front();
while (q.size() && a[i] <= a[q.back()])
q.pop_back();
q.push_back(i);
// if (i >= m)
cout << a[q.front()] << endl;
}
}
/*
6 3
7 8 1 4 3 2
00 07 78 81 14 43
不必多言
*/