【总结】队列与栈

进出在同一侧,且满足后进先出,先进后出

车厢调度(train)
有一个火车站,铁路如图所示,每辆火车从A驶入,再从B方向驶出,同时它的车厢可以重新组合。假设从A方向驶来的火车有n节(n≤1000),分别按照顺序编号为1,2,3,…,n。假定在进入车站前,每节车厢之间都不是连着的,并且它们可以自行移动到B处的铁轨上。另外假定车站C可以停放任意多节车厢。但是一旦进入车站C,它就不能再回到A方向的铁轨上了,并且一旦当它进入B方向的铁轨,它就不能再回到车站C。
在这里插入图片描述

负责车厢调度的工作人员需要知道能否使它以a1,a2,…,an的顺序从B方向驶出,请来判断能否得到指定的车厢顺序。

解析:
观察发现,整个调度过程其实是在模拟入栈出栈的过程,而这个过程中,我们可以分成三种状态:栈前、栈中、栈后。我们可以发现,当某个数字出栈了,说明比它小的数字要么已经出栈了,要么还在栈里,不能是入栈前状态,并且在栈中的顺序是从大到小的(从栈顶往栈底看),比如出5,那么1,2,3,4要么已经在5之前出了,要么还在栈中(假如1,3,4在栈中,从栈顶往栈底看依次为4,3,1),不能是入栈前的状态。如果某个数字要出栈,那么当前在栈中的数字都必须小于它,否则就与栈的性质矛盾,不合法,于是我们可以这样解决:

从第一个数字开始扫描,a[i]表示当前出栈的数字,如果有比a[i]大的数字还在栈中,那么就产生矛盾,输出“NO”;否则,标记当前数字a[i]为栈后状态,那么[1, a[i]-1]这些数字如果还没出栈,标记为栈中状态。具体我们可以用0表示为确定状态,1表示栈中状态,2表示栈后状态。

#include<cstdio>
#include<stack>
using namespace std;
stack<int> liu;
int n,a[1005],b[1005];
int main() {
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&b[i]);
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=n;j++) {
			if(b[i]==j) a[j]=2;
			if(a[j]==2) continue;
			else if(b[i]>j) {
				if(a[j]==0) a[j]=1;
			}
			else if(b[i]<j) {
				if(a[j]>0) {
					printf("NO");
					return 0;
				}
			}
		}
	}
	printf("YES");
}

字符串匹配问题(strs)
字符串中只含有括号 (),[],<>,{},判断输入的字符串中括号是否匹配。如果括号有互相包含的形式,从内到外必须是<>,(),[],{},例如。输入: [()] 输出:YES,而输入([]),([)]都应该输出NO。

解析:简历四个栈,记录四种符号

  • 由于有优先级,所以当一个右括号匹配时,比他优先级低的括号必须已经匹配完成才行,左括号入栈即可,最后判断是否匹配完
#include<cstdio>
#include<stack>
#include<cstring>
using namespace std;
stack<int> a,b,c,d;
char s[300];
int n,len,flag;
int main() {
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
		flag=1;
		scanf("%s",s);
		len=strlen(s);
		for(int i=0;i<len;i++) {
			if(s[i]=='{') {
				a.push(1);
			}
			if(s[i]=='}') {
				if(a.empty()==1) {
					flag=0;
					break;
				}
				a.pop();
				if(b.empty()==0||c.empty()==0||d.empty()==0) {
					flag=0;
					break;
				}
			}
			if(s[i]=='[') {
				b.push(1);
			}
			if(s[i]==']') {
				if(b.empty()==1) {
					flag=0;
					break;
				}
				b.pop();
				if(c.empty()==0||d.empty()==0) {
					flag=0;
					break;
				}
			}
			if(s[i]=='(') {
				c.push(1);
			}
			if(s[i]==')') {
				if(c.empty()==1) {
					flag=0;
					break;
				}
				c.pop();
				if(d.empty()==0) {
					flag=0;
					break;
				}
			}
			if(s[i]=='<') {
				d.push(1);
			}
			if(s[i]=='>') {
				if(d.empty()==1) {
					flag=0;
					break;
				}
				d.pop();
			}
		}
		//判断是否匹配完
		if(a.empty()==0||b.empty()==0||c.empty()==0||d.empty()==0) {
			flag=0;
		}
		if(flag) printf("YES\n");
		else {//多组数据,要清空
			printf("NO\n");
			while(a.empty()==0) a.pop();
			while(b.empty()==0) b.pop();
			while(c.empty()==0) c.pop();
			while(d.empty()==0) d.pop();
		}
	}
}

中缀表达式值(expr)
前缀和后缀表达式都很容易求值,我们将中缀表达式转换成后缀表达式,且后缀表达式不需要括号。复杂度O(n)

  1. 建立一个符号栈,一个数字栈
  2. 如果遇到一个数,加入数字栈
  3. 如果遇到左括号,把左括号入符号栈
  4. 如果遇到右括号,不断取出符号栈顶并在数字栈中运算,直到栈顶为左括号,把左括号出栈
  5. 如果遇到运算符,**只要栈顶符号的优先级不低于新符号,就不断取出栈顶并运算,最后把新括号入栈。**优先级乘除乘方>加减>左括号
  6. 最终数字栈剩下的数字就是结果。
    分析:第五点保证了先进先出(优先级相同时)以及符号相互的优先级,第四点其实就是处理括号内的运算,在符号栈中越先出去的,在后缀运算中优先级更高
#include<cstdio>
#include<cmath>
#include<stack>
#include<cstring>
using namespace std;
//stack<char> f;
const int MAXN=1e6+5;
char a[MAXN],f[MAXN];
long long n,m,t,b[MAXN];
int main() {
	scanf("%s",a);
	n=strlen(a);
	for(int i=0;i<n;i++) {
		if(a[i]>='0'&&a[i]<='9') {
			int w=0;
			while(a[i]>='0'&&a[i]<='9') w=w*10+(a[i]-'0'),i++;
			b[++m]=w;
		} 
		if(a[i]=='(') f[++t]='(';
		else if(a[i]==')') {
			while(f[t]!='(') {
				char x=f[t];
				t--;
				if(x=='+') b[m-1]=b[m-1]+b[m],m--;
				if(x=='-') b[m-1]=b[m-1]-b[m],m--;
				if(x=='*') b[m-1]=b[m-1]*b[m],m--;
				if(x=='/') b[m-1]=b[m-1]/b[m],m--;
				if(x=='^') b[m-1]=pow(b[m-1],b[m]),m--;
			}
			t--;
		}
		else if(a[i]=='*'||a[i]=='/'||a[i]=='^') {
			while(f[t]=='*'||f[t]=='/'||f[t]=='^') {
				char x=f[t];
				t--;
				if(x=='*') b[m-1]=b[m-1]*b[m],m--;
				if(x=='/') b[m-1]=b[m-1]/b[m],m--;
				if(x=='^') b[m-1]=pow(b[m-1],b[m]),m--;
			}
			f[++t]=a[i];
		}
		else if(a[i]=='+'||a[i]=='-') {
			while(f[t]=='+'||f[t]=='-'||f[t]=='*'||f[t]=='/'||f[t]=='^') {
				char x=f[t];
				t--;
				if(x=='*') b[m-1]=b[m-1]*b[m],m--;
				if(x=='/') b[m-1]=b[m-1]/b[m],m--;
				if(x=='^') b[m-1]=pow(b[m-1],b[m]),m--;
				if(x=='+') b[m-1]=b[m-1]+b[m],m--;
				if(x=='-') b[m-1]=b[m-1]-b[m],m--;
			}
			f[++t]=a[i];
		}
		/*for(int i=1;i<=m;i++) printf("%d ",b[i]);
		printf("\n");*/
	}
	for(int i=t;i>=1;i--) {
		char x=f[i];
		if(x=='+') b[m-1]=b[m-1]+b[m],m--;
		if(x=='-') b[m-1]=b[m-1]-b[m],m--;
		if(x=='*') b[m-1]=b[m-1]*b[m],m--;
		if(x=='/') b[m-1]=b[m-1]/b[m],m--;
		if(x=='^') b[m-1]=pow(b[m-1],b[m]),m--;
	}
	printf("%lld",b[1]);
}

括号画家
写累了,直接看代码吧:
错误代码

#include<cstdio>
#include<cmath>
#include<stack>
#include<cstring>
#include<algorithm>
using namespace std;
stack<int> f;
const int MAXN=100005;
char s[MAXN];
int n,ans,t;
int main() {
	scanf("%s",s);
	n=strlen(s);
	for(int i=0;i<n;i++) {
		t++;
		if(s[i]=='('||s[i]=='['||s[i]=='{') f.push(s[i]);
		else if(f.empty()==1||(s[i]==')'&&f.top()!='(')||(s[i]==']'&&f.top()!='[')||(s[i]=='}'&&f.top()!='{')) {
			t=0;
			while(f.empty()==0) f.pop();
		}
		else f.pop();
		int m=f.size();
		ans=max(ans,t-m);
	}
	printf("%d",ans);
}

LJS的数据:
( ( ( { } [ ] ( ( ( )
正确输出:4
我的输出:6

为什么会这样呢?会发现我并没有考虑多出来的没有匹配的括号,计算时去掉了f.size()即栈的长度,如果在最左边,这种算法是成立的;而如果未匹配到的在中间,就是不满足题意,而我相当于把两个满足要求的子序列合并在一起了。只需判断当前子序列末尾与栈的是否完全相同即可

正解:

#include<cstdio>
#include<cmath>
#include<stack>
#include<cstring>
#include<algorithm>
using namespace std;
stack<int> f;
const int MAXN=100005;
char s[MAXN];
int n,ans,t;
int main() {
	scanf("%s",s);
	n=strlen(s);
	for(int i=0;i<n;i++) {
		t++;
		if(s[i]=='('||s[i]=='['||s[i]=='{') f.push(s[i]);
		else if(f.empty()==1||(s[i]==')'&&f.top()!='(')||(s[i]==']'&&f.top()!='[')||(s[i]=='}'&&f.top()!='{')) {
		//说明以后的子序列不能延续这以前的部分了,直接删除
			t=0;
			while(f.empty()==0) f.pop();
		}
		else f.pop();
		int m=f.size();
		bool flag=1;
		if(m!=0) {
			int b[MAXN]={},len=0;
			while(f.empty()==0) {
				b[++len]=f.top();
				f.pop();
			}
			for(int j=i-t+1;j<=i-t+m;j++) {
				if(s[j]!=b[m-(j-(i-t+1))]) flag=0;
			} 
			for(int i=len;i>=1;i--) f.push(b[i]);
			if(flag) ans=max(ans,t-m);
		}
		if(flag) ans=max(ans,t-m);
	}
	printf("%d",ans);
}

队列

进出在异侧,且满足先进先出,后进后出

占卜DIY

#include <cstdio>
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
//队列
//上次没有在操作完后统计,必须操作完后再统计
const int MAXN = 105;
queue<int> a[MAXN];
int x, ans, b[MAXN];
char c[MAXN];
//这道题从一边进入,另一边取出,且进入和取出的方向是固定的,所以用队列
//因为栈是从同一边进入和取出
int main() {
    for (int i = 1; i <= 12; i++) {
        for (int j = 1; j <= 4; j++) {
            cin >> c[j];
            if (c[j] == '0')
                c[j] = 10;
            else if (c[j] > '0' && c[j] <= '9')
                c[j] -= '0';
            else if (c[j] == 'A')
                c[j] = 1;
            else if (c[j] == 'J')
                c[j] = 11;
            else if (c[j] == 'Q')
                c[j] = 12;
            else if (c[j] == 'K')
                c[j] = 13;
        }
        for (int j = 4; j >= 1; j--) a[i].push(c[j]);
        //注意队列的方向
    }
    for (int j = 1; j <= 4; j++) {
        cin >> c[j];
        if (c[j] == '0')
            c[j] = 10;
        else if (c[j] > '0' && c[j] <= '9')
            c[j] -= '0';
        else if (c[j] == 'A')
            c[j] = 1;
        else if (c[j] == 'J')
            c[j] = 11;
        else if (c[j] == 'Q')
            c[j] = 12;
        else if (c[j] == 'K')
            c[j] = 13;
        a[13].push(c[j]);
        //注意队列的方向
    }
    for (int i = 1; i <= 4; i++) {
        x = a[13].front(), a[13].pop();
        while (1) {
            if (x >= 100)
                x -= 100;
            if (x == 13) {
                break;
            }
            int t = x;
            a[t].push(x + 100);
            x = a[t].front();
            a[t].pop();
        }
    }
    for (int i = 1; i <= 12; i++) {
        while (a[i].empty() == 0) {
            int t = a[i].front();
            a[i].pop();
            if (t > 100)
                b[t - 100]++;
        }
    }
    for (int i = 1; i <= 12; i++) {
        if (b[i] == 4)
            ans++;
    }
    printf("%d", ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值