一、栈
1.单调栈
P2947 [USACO09MAR] Look Up S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
典型的单调栈题型。
cpp
#include <iostream>
using namespace std;
#include <algorithm>
#include<map>
#include <unordered_map>
#include <vector>
#include <queue>
#include <stack>
stack<int> s;//存入的是下标
vector<int> nums;
int main(){
int n;
cin>>n;
vector<int> result(n,0);
for(int i=0;i<n;i++){
int x;
cin>>x;
nums.push_back(x);
while(!s.empty()&&x>nums[s.top()]){
result[s.top()]=i+1;
s.pop();
}
s.push(i);
}
for(int i=0;i<n;i++){
cout<<result[i]<<endl;
}
return 0;
}
2.栈
1363 -- Rails (poj.org)(栈的出栈顺序问题)
题目大意: A站有编号为1到N,N最大1000,的车厢,车厢进入中转station了就不能回到A,只能停在station内或者进入B站,问能不能按照给定的顺序排成那样的车厢号。
解题: 每次一个新车厢进入station前,检查栈内栈顶元素是否与B站没有匹配的车厢头是否相等(如果有,则弹栈,重复此步骤),没有匹配的直接入栈。最后栈为空则可以排成给定次序。
举个例子:(2,1,3,5,4)能不能排成这样。
cpp
#include <iostream>
using namespace std;
#include <algorithm>
#include<map>
#include <vector>
#include <queue>
#include <stack>
#include<string>
int N;
string StackOrder(vector<int> seq){
stack<int> s;
int cnt=0;
for(int i=1;i<=N;i++){
while(!s.empty()&&s.top()==seq[cnt]){
s.pop();
cnt++;
}
s.push(i);
}
while(!s.empty()&&s.top()==seq[cnt]){
s.pop();
cnt++;
}
if(s.empty()){
return "Yes";
}else{
return "No";
}
}
int main(){
while (true) {
cin >> N; // 读取 N
if (N == 0) break; // 如果 N 是 0,结束输入
while (true) {
vector<int> Seq(N);
cin >> Seq[0]; // 读取排列的第一个数
if (Seq[0] == 0) break; // 如果第一个数是 0,结束当前块
// 读取排列的剩余部分
for (int i =1; i < N; i++) {
cin >> Seq[i];
}
cout<<StackOrder(Seq)<<endl;
}
cout<<endl;
}
return 0;
}
2.1686 -- Lazy Math Instructor (poj.org)
我的主要想法就是中缀表达式转换为后缀表达式(逆波兰)
中缀表达式 : a + b
前缀表达式 : + a b
后缀表达式 : a b +
-
转换过程:(中缀转后缀)
-
遇到操作数(如 a,b,c,2a,b,c,2),直接添加到输出列表。
-
遇到运算符,与栈顶的运算符比较优先级:
-
如果栈顶运算符优先级较高或相等,则弹出栈顶运算符并添加到输出列表,重复此过程直到栈顶运算符优先级较低或栈为空。
-
将当前运算符压入栈中。
-
-
遇到左括号
(
,压入栈中。 -
遇到右括号
)
,弹出栈中的运算符并添加到输出列表,直到遇到左括号(
,左括号弹出但不输出。 -
表达式扫描完毕后,将栈中剩余的运算符依次弹出并添加到输出列表。
-
当转换为逆波兰表达式后就可以求值(类似题目):150. 逆波兰表达式求值 - 力扣(LeetCode)
cpp
class Solution {
public:
bool isNumber(string& token) {
return !(token == "+" || token == "-" || token == "*" || token == "/");
}
int evalRPN(vector<string>& tokens) {
stack<int> nums;
for(int i=0;i<tokens.size();i++){
if(isNumber(tokens[i])) nums.push(atoi(tokens[i].c_str()));
else{
int num2=nums.top();nums.pop();//注意后入的在后
int num1=nums.top();nums.pop();
if (tokens[i] == "+") {
nums.push(num1 + num2);
} else if (tokens[i] == "-") {
nums.push(num1 - num2);
} else if (tokens[i] == "*") {
nums.push(num1 * num2);
} else if (tokens[i] == "/") {
nums.push(num1 / num2);
}
}
}
return nums.top();
}
};
还可以写的更优雅:
cpp
class Solution {
public:
int evalRPN(vector<string>& tokens) {
unordered_map<string,function<int(int,int)>> map={
{"+",[](int a,int b){return a+b;}},
{"-",[](int a,int b){return a-b;}},
{"*",[](int a,int b){return a*b;}},
{"/",[](int a,int b){return a/b;}}
} ;
stack<int> st;
for(string& s:tokens)
{
if(map.count(s))
{
int right=st.top();st.pop();
int left=st.top();st.pop();
st.push(map[s](left,right));
}
else
st.push(stoi(s));
}
return st.top();
}
};
对于此题:
1.转化为逆波兰表达式2.计算比较
cpp
#include <iostream>
using namespace std;
#include <algorithm>
#include<map>
#include <vector>
#include <queue>
#include <stack>
#include<string>
map<char,int> m;
string transform_str(string str){
string re="";
stack<char> punc;
for(int i=0;i<str.size();i++){
if(isalnum(str[i])) re+=str[i];
else{
switch(str[i]){
case '(': punc.push(str[i]);break;
case ')':
while(punc.top()!='('){
re+=punc.top();
punc.pop();
}
punc.pop();
break;
case '+':
case '-':
case '*':
while(!punc.empty()&&m[str[i]]<=m[punc.top()]){
re+=punc.top();
punc.pop();
}
punc.push(str[i]);
break;
}
}
}
while(!punc.empty()){
re+=punc.top();
punc.pop();
}
return re;
}
int result(string nt){
stack<int> nums;
for(int i=0;i<nt.length();i++){
char c=nt[i];
if(isalnum(c)){
if(isdigit(c)) {
nums.push(c-'0');
}else{
nums.push(c);//字母用其ASCII代替
}
}
else{
int num2=nums.top();nums.pop();
int num1=nums.top();nums.pop();
switch(c){
case '+': nums.push(num1+num2);break;
case '-': nums.push(num1-num2);break;
case '*': nums.push(num1*num2);break;
}
}
}
return nums.top();
}
//void test(){
// string s;
// cin>>s;
// string r=transform_str(s);
// cout<<r<<endl;
// cout<<result(r)<<endl;
//};
int main(){
m['(']=0;m['+']=m['-']=1;m['*']=2;
int T;
cin>>T;
cin.get();
while(T--){
string str1,str2;
getline(cin,str1);getline(cin,str2);
string n1=transform_str(str1); string n2=transform_str(str2);
if(result(n1)==result(n2)) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
oj平台有些问题,还在等待。
3.2106 -- Boolean Expressions (poj.org)
F是0,V是1,运算符有& ! | ( )这五个,优先级为! > & > |当然有括号肯定先算括号里的,中间会有若干空格。
我的思路与上题一模一样,就是现将输入的式子去除空格,然后根据优先级去掉括号形成后缀表达式,然后直接利用栈进行计算结果。
cpp
#include <iostream>
using namespace std;
#include <algorithm>
#include<map>
#include <vector>
#include <queue>
#include <stack>
#include<string>
map<char,int> m;
string transform_str(string str){
string re="";
stack<char> punc;
for(int i=0;i<str.size();i++){
if(isalnum(str[i])) re+=str[i];
else{
switch(str[i]){
case '(': punc.push(str[i]);break;
case ')':
while(punc.top()!='('){
re+=punc.top();
punc.pop();
}
punc.pop();
break;
case '|':
case '&':
case '!':
while(!punc.empty()&&m[str[i]]<=m[punc.top()]){
re+=punc.top();
punc.pop();
}
punc.push(str[i]);
break;
}
}
}
while(!punc.empty()){
re+=punc.top();
punc.pop();
}
return re;
}
char result(string nt){
stack<bool> nums;
for(int i=0;i<nt.length();i++){
char c=nt[i];
if(c=='V'){
nums.push(1);
}
else if(c=='F'){
nums.push(0);
}
else{
bool log1, log2;
switch(c){
case '|':
log1=nums.top();nums.pop();
log2=nums.top();nums.pop();
nums.push(log1|log2);
break;
case '&':
log1=nums.top();nums.pop();
log2=nums.top();nums.pop();
nums.push(log1&log2);
break;
case '!':
log1=nums.top();nums.pop();
nums.push(!log1);
break;
}
}
}
return nums.top()==1?'V':'F';
}
string getNoSpace(string s){
string line="";
for (char c : s) {
if (!isspace(c)) { // 如果不是空格
line+=c;
}
}
return line;
}
//void test(){
// string s;
// getline(cin,s);
// s=getNoSpace(s);
// string r= transform_str(s);
// cout<<r<<endl;
// cout<<result(r);
//};
int main() {
m['('] = 0;
m['|'] = 1;
m['&'] = 2;
m['!'] = 3;
string str1;
//test();
int cnt=1;
while(getline(cin, str1)) {
string str = getNoSpace(str1);
string n1 = transform_str(str1);
cout << "Expression " << cnt++ << ": " << result(n1) << endl;
}
return 0;
}
同样:
二、队列
1.基础知识
1.3210 A Stack or A Queue? - ZOJ Problem Set (pintia.cn)(基础主要是队列与栈结构(简单))
cpp
#include <iostream>
using namespace std;
#include <algorithm>
#include<map>
#include <unordered_map>
#include <vector>
#include <queue>
#include <stack>
#include<string>
int N;
bool isStack(vector<int> in,vector<int> out){
int i=0,j=N-1;
for(;i<j;i++,j--){
if(in[j]!=out[i]) return false;
}
return true;
}
bool isQueue(vector<int> in,vector<int> out){
for(int i=0;i<N;i++){
if(in[i]!=out[i]) return false;
}
return true;
}
int main(){
int T;
cin>>T;
while(T--){
cin>>N;
int x;
vector<int> in;
vector<int> out;
for(int i=0;i<N;i++){
cin>>x;
in.push_back(x);
}
for(int i=0;i<N;i++){
cin>>x;
out.push_back(x);
}
bool a= isStack(in,out),b=isQueue(in,out);
if(a&&b){
cout<<"both"<<endl;
}else if(b){
cout<<"queue"<<endl;
}else if(a){
cout<<"stack"<<endl;
}else{
cout<<"neither"<<endl;
}
}
return 0;
}
2.1948 Team Queue - ZOJ Problem Set (pintia.cn)(队列的小变化(模拟))
cpp
#include <iostream>
using namespace std;
#include <algorithm>
#include<map>
#include <unordered_map>
#include <vector>
#include <queue>
#include <stack>
#include<string>
#define MAX 1001
int main(){
int cnt=1;
while(1){
queue<int> teamQueue[MAX];//每个元素是队列的数组
queue<int> team;
map<int,int> m;
int t;
cin>>t;
if(t==0) break;
cout<<"Scenario #"<<cnt++<<endl;
while(t--){
int num;
cin>>num;
for(int i=0;i<num;i++){
int x;
cin>>x;
m[x]=t;//倒序
}
}
while(true){
string op;
cin>>op;
if(op=="ENQUEUE"){
int x;
cin>>x;
int t=m[x];
if(teamQueue[t].empty()) team.push(t);
teamQueue[t].push(x);
}else if(op=="DEQUEUE"){
if(!team.empty()){
int t=team.front();
cout<<teamQueue[t].front()<<endl;
teamQueue[t].pop();
if(teamQueue[t].empty()) team.pop();
}
}else{
cout<<endl;
break;
}
}
}
return 0;
}
3.P1540 [NOIP2010 提高组] 机器翻译 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
(模拟题,用数组其实更简单,本来我想用queue,但是无法遍历,利用deque(双端队列)就可以遍历)
cpp
#include <iostream>
using namespace std;
#include <algorithm>
#include<map>
#include <unordered_map>
#include <vector>
#include <queue>
#include <stack>
#include<string>
deque<int> q;
bool findQ(int x){
for(int val:q){
if(x==val) return true;
}
return false;
}
int main(){
int N,M;
cin>>M>>N;
int cnt=0;
for(int i=0;i<N;i++){
int x;
cin>>x;
if(q.empty()){
q.push_back(x);
cnt++;
continue;
}
if(!findQ(x)){
if(q.size()<M){
q.push_back(x);
cnt++;
}else{
q.pop_front();
q.push_back(x);
cnt++;
}
}
}
cout<<cnt;
return 0;
}
4.3629 -- Card Stacking (poj.org)
思路:利用队列模拟过程。
代码:
cpp
#include <iostream>
using namespace std;
#include <algorithm>
#include<map>
#include <vector>
#include <queue>
#include <stack>
#include<string>
queue<int> q;
vector<int> result;
//void test(){
//};
int main() {
int N,K,P;
cin>>N>>K>>P;//K=nN,P is card,M=n,K-n=n(N-1)
for(int i=1;i<=K;i++){
q.push(i);
}
int cnt=1;
while(!q.empty()){
if(cnt%N==0){
result.push_back(q.front());
}
q.pop();//给右边
for(int i=0;i<P;i++){
int c=q.front();q.pop();q.push(c);//P张
}
cnt++;
}
sort(result.begin(),result.end());
for(int i=0;i<result.size();i++){
cout<<result[i]<<endl;
}
return 0;
}
思路:首先我们建立一个队列,然后建立一个结构体,用一个flag标记位表示我们关注的作业,然后我们按照要求模拟队列,解决问题。
cpp
#include <iostream>
using namespace std;
#include <algorithm>
#include<map>
#include <vector>
#include <queue>
#include <stack>
#include<string>
//void test(){
//};
struct work{
int prior;
int flag;
};
int maxnum(queue<work> q){
int maxv=-1;
queue<work> temp = q;
while (!temp.empty()) {
maxv = max(maxv, temp.front().prior);
temp.pop();
}
return maxv;
}
int main() {
int T;scanf("%d",&T);
queue<work> q;
int n,m;
while(T--){
cin>>n>>m;
int maxv=-1;
for(int i=0;i<n;i++){
int c;cin>>c;
if(i==m) q.push({c,1});
else q.push({c,0});
maxv = max(maxv, c);
}
int time=0;
while(!q.empty()){
if(q.front().prior>=maxv){
time++;
if(q.front().flag) break;
//更新maxv
q.pop();
maxv=maxnum(q);
}
else{
work c=q.front();q.pop();
q.push(c);
}
}
cout<<time<<endl;
// 清空队列
while (!q.empty()) {
q.pop();
}
}
return 0;
}
6.p12207.pdf (onlinejudge.org)
思路:
模拟一个队列系统,处理两种特定的队列操作。每个测试用例有一个队列,队列中有初始的一系列任务编号(从1到p),并且接下来有一系列操作。操作可以是两种类型:
-
N
(Next):打印当前队列的队首元素,并将队首元素移到队尾。这相当于处理队列中的下一个任务,完成之后将其重新排队。 -
E x
(Enter) :将元素x
移动到队首。如果队列中已有这个元素,则首先移除它,然后将它重新插入到队首。这个操作可以视作将某个任务优先处理。
通过使用 deque
(双端队列)来高效实现这两种操作,因为 deque
允许从队列两端高效地插入和删除元素。每次执行完操作后,根据需求输出队列的状态。程序通过 scanf
和 getchar
读取输入并执行相应的操作,最后输出每个操作后的结果。
cpp
#include <iostream>
using namespace std;
#include <algorithm>
#include<map>
#include <unordered_map>
#include <vector>
#include <queue>
#include <stack>
#include<string>
const int C = 1000;
int main()
{
int p, c, caseno = 0;
while(~scanf("%d%d", &p, &c) && (p || c)) {
deque<int> dq;
printf("Case %d:\n", ++caseno);
for(int i = 1; i <= p && i <= C; i++) dq.push_back(i);
while(c--) {
char c = getchar();
if((c = getchar()) == 'N') {
printf("%d\n", dq.front());
dq.push_back(dq.front());
dq.pop_front();
} else if(c == 'E') {
int x;
scanf("%d", &x);
for(deque<int>::iterator iter = dq.begin(); iter != dq.end(); iter++)
if(*iter == x) {
dq.erase(iter);
break;
}
dq.push_front(x);
}
}
}
return 0;
}
uva账户邮箱一直没有验证码,没有注册。
2.滑动窗口
1.P1886 滑动窗口 /【模板】单调队列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
利用优先队列结合队列解决。
cpp
#include <iostream>
using namespace std;
#include <algorithm>
#include<map>
#include <unordered_map>
#include <vector>
#include <queue>
#include <stack>
#include<string>
priority_queue<int,vector<int>,greater<int>> q_min;
queue<int> q;
vector<int> v;
vector<int> min_num;
vector<int> max_num;
int main() {
int n,k;
cin>>n>>k;
for(int i=0;i<n;i++){
int m;
cin>>m;
v.push_back(m);
}
for (int i=0;i<k;i++){
q.push(v[i]);
}
//find the min
for(int i=k;i<=n;i++){
queue<int> q_temp;
while (!q.empty()) {
q_min.push(q.front());
q_temp.push(q.front());
q.pop();
}
min_num.push_back(q_min.top());
while (q_min.size()!=1) {
q_min.pop();
}
max_num.push_back(q_min.top());
q_min.pop();
//update the new,and pop the old
while (!q_temp.empty()) {
q.push(q_temp.front());
q_temp.pop();
}
q.pop();
q.push(v[i]);
}
for(auto it=min_num.begin();it!=min_num.end();it++){
cout<<*it<<" ";
}
cout<<endl;
for(auto it=max_num.begin();it!=max_num.end();it++){
cout<<*it<<" ";
}
return 0;
}
int main(){
return 0;
}
会超时
还可以利用其他办法:(单调队列)
cpp
#include <iostream>
using namespace std;
#include <algorithm>
#include<map>
#include <unordered_map>
#include <vector>
#include <queue>
#include <stack>
#include<string>
const int N=1e6+10;
#include <deque>
deque<int> dq;
int a[N];
int n,m;
int main(){
scanf("%d%d",&n,&m);
//最小值
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
while(dq.size()&&a[dq.back()]>a[i]) dq.pop_back();
dq.push_back(i);
if(i>=m){
if(dq.front()<=i-m&&dq.size()) dq.pop_front();//为了限制这个数必须在这个滑动窗口里面
cout<<a[dq.front()]<<" ";
}
}
cout<<endl;
dq.erase(dq.begin(),dq.end());
//最大值
for(int i=1;i<=n;i++){
while(dq.size()&&a[dq.back()]<a[i]) dq.pop_back();
dq.push_back(i);
if(i>=m){
if(dq.front()<=i-m&&dq.size()) dq.pop_front();
cout<<a[dq.front()]<<" ";
}
}
return 0;
}