蓝桥杯算法入门_05

#include <iostream>
using namespace std;
#include<stack>
#include<vector>
//栈与递归

/*栈的操作 stack
只能在栈顶插入或删除
push()  入栈     无返回值
pop()	出栈 	无返回值
top()	取栈顶元素  返回T类型
empty()	栈为空 		bool类型  true为空
size()  统计元素个数   非负整数(size_t类型)
*/

//手动实现栈 (简陋版)

struct Stack {
	int data[10000];
	int top = -1;  //指向栈顶元素

	void push(int x) {
		top++;
		if(top < 10000) {
			data[top] = x;
		} else {
			top--;
			cout<<"stack overflow "<<endl;  //栈溢出
		}
	}

	void pop() {
		if(top >= 0) {
			top--;
		}
	}

	int topval() { //栈为空也要有返回值
		if(top >= 0) {
			return data[top];
		} else {
			return -1;
		}
	}

};

void test_01() {
	Stack s;
	for(int i = 1; i <= 10; i++) {
		s.push(i);
	}
	for(int i = 1; i <= 10; i++) {
		cout<< s.topval() <<" ";  //无法持续遍历 ,只能边取出变查看  top -- pop
		s.pop();
	}
	return;
}

/*应用
①火车出入站 分车厢  (栈的入栈元素 的出栈顺序 是否符合规则)
②括号匹配
*/

/*火车出入站  栈的元素顺序 合法性检测

栈s 按 1 -- n 顺序入栈
判断出栈顺序是否可以为 排列a

a元素个数
排列a

输入:
5
1 3 2 5 4

输出:
legal

----------
输入:
5
1 5 3 2 4

输出:
illegal


*/

void test_02() {
	int n;
	cin >> n;
	vector<int> a(n);  //初始长度n ,默认值0
	for(int i = 0; i < n; i++) {
		cin >> a[i];
	}
	stack<int> s;
	int cur = 1; //当前
	bool f = 1; //判断标记,1为合法
	for(int i = 0; i < n; i++) {
		while((s.empty() || s.top() != a[i] && cur <= n)) {//栈空时停止循环  ,当前大于n时,退出循环
			s.push(cur); //按 1 -- n顺序入栈 (等于a[i]即找到元素时,出栈 进行判断)
			cur++;
		}
		if(s.empty() || s.top() != a[i] ) { //栈为空时还未找完a[i]序列则不合法:
			f = 0;								//(全部出栈都找不到,说明在之前的判断中元素出栈了,即序列不合法)
			break;
		} else {
			s.pop();  //遍历出栈  直到找到栈顶元素
		}
	}
	if(f) {
		cout<< "legal" <<endl;
	} else {
		cout<< "illegal" <<endl;
	}
	return;
}

/*递归
n! = n * (n-1)!

例子 3!

int factorial(1){
		return 1;
}
int factorial(2){
	return 2*factorial(1);
}
int factorial(3){
	return 3*factorial(2);  // 3 * 2*factorial(1) == 3 * 2 * 1
}


递归深度 -- 从最后执行的出口开始返回

递归易超时!

*/
int factorial(int n) {
	if(n == 1) {	//边界条件
		return 1;
	}
	return n*factorial(n - 1);
}

int Fix(int n) {
	if(n == 1 || n == 2) { //出口
		return 1;
	}
	return Fix(n - 1) + Fix(n - 2);
}

/*实现 f(x) 函数      (-1e5 <= x <= 1e5)
0					x <= 0
1					x = 1
3*f(x/2) - 1		x > 1 && x % 2 ==0
3*f((x+1)/2) - 1	x > 1 && x % 2 ==1

*/
long long f_3(int x) {  //蓝桥杯 long long 输出格式建议 %I64d
	if(x <= 0) {
		return 0;  //分四段
	}
	if(x == 1) {
		return 1;
	}
	if(x > 1 && x % 2 == 0) {
		return 3 * f_3(x/2) - 1;
	}
	if(x > 1 && x % 2 == 1) {
		return 3*f_3((x+1)/2) - 1;
	}
}

void test_03() {
	int x;
	scanf("%d",&x);
	printf("%I64d",f_3(x));  //  !!!!!!
}


/*汉诺塔   栈结构
A B C


分三步:
1. n - 1 个盘子 从 A 到 B ,剩下最后一个盘子
2. A的最后一个盘子直接 A 到 C
3. 再把 n-1 个盘子 B 到 C

归为两种操作:
1.把多个盘子从一个柱子移动到另一个柱子,移第三个柱子为中介
2.把一个盘子从一个柱子_直接_移动到另一个柱子

*/

stack<int> S[3]; //二维   3柱子 每个柱子上 多个盘
void move(int x,int y) { //2.把一个盘子从一个柱子_直接_移动到另一个柱子
	int temp = S[x].top(); //temp 移动的盘子
	S[x].pop();  //x 柱子1
	S[y].push(temp); //y 柱子2
	cout << x << "-->" << y <<endl;
}
void hanio(int A,int B,int C,int n) { //1.把多个盘子从一个柱子移动到另一个柱子,移第三个柱子为中介
	if(n == 1) {
		move(A,C);
		return;
	}
	//一次完整的移动
	hanio(A,C,B,n - 1);  // A -- >C , B辅助 ,移动盘子的数量
	move(A,C);	//最后一个剩余盘子 A --> C
	hanio(B,A,C,n - 1); // B --> A , C辅助  ,移动盘子的数量

}

void test_04() {
	int n;
	cin >> n;
	for(int i = n; i >= 1; i--) {
		S[0].push(i);
	}
	hanio(0,1,2,n);  //分别代表A、B、C ,初始移动 n个盘子
	while(!S[2].empty()) { //输出移动后 C柱上的盘子
		cout << S[2].top() << " ";  //需控制行号空格
		S[2].pop();
	}
}


/*(写的函数已经最少次数 但递归时间超时 2 ^64)
求最小移动次数 和消耗体力(移动到第i号圆盘耗费i点体力)

f[n] =  f[n - 1] + 1 + f[n - 1]   //每一步移动盘子的三步走  移动次数
g[n] = g[n - 1] + n + g[n - 1]   //每一步移动盘子的三步走 	消耗体力

推导 次数 == 2^n - 1   别用pow(2,n) //大数会不准确

输入 盘子数量
输出 次数 体力

样例输入:
3

输出:
7 11

*/
long long f[65],g[65];  // (1<=n<=60)    数值可能到达 2 ^ 60
void test_05() {
	int n;
	scanf("%d",&n);
	f[1] = 1;
	for(int i = 2; i <= n; i++) {
		f[i] = 2* f[i - 1] + 1;  //推导次数公式
	}
	g[1] = 1;
	for(int i = 2; i <= n; i++) {
		g[i] = 2 * g[i - 1] + i;  //记录到 i次时,总消耗体力
	}
	printf("%I64d %I64d\n",f[n],g[n]);
}

/*猴子吃桃
每天吃一半还多一个  == x/2 + 1
到第n天,剩下一个桃子 ,问开始共有多少个桃子


*/
int n_6;
int f_6(int x) {
	if(x == n_6) {
		return 1;
	} else {
		return (f_6(x + 1) + 1) * 2;
	}
}
void test_06() {

	scanf("%d",&n_6);
	printf("%d",f_6(1));
}

/*斐波那契数列 变式
f(1) = f(2) = 1;
f(n) = a*f(n - 1) + b * f(n - 2)

输入 n ∈[1,100] , a∈[1,10] , b∈[1,10] ,p∈[1,2000]

输出f(n)对p取模的值

样例输入:
3 1 1 1000

输出:
2


*/
int ans_07[2005];
bool vis_07[2005];
int f_07(int n,int a,int b,int p) {
	if(vis_07[n]) {
		return ans_07[n];
	}
	vis_07[n] = true;
	if(n == 1 || n == 2) {
		return	ans_07[n] = 1 % p;     //等效返回ans[n]
	} else {
		return ans_07[n] = (a * f_07(n - 1,a,b,p) % p + b * f_07(n - 2,a,b,p) % p )%p;  //依照题目   % p每次取模防止溢出
	}
}

void test_07() {
	int n,a,b,p;
	scanf("%d%d%d%d",&n,&a,&b,&p);
	printf("%d\n",f_07(n,a,b,p));
}

/*计算 x^y   (主要是时间复杂度)

题目给出了快速幂的写法:(快速幂 :每次返回的结果再平方 到达ans循环次数减少)
		 |	f(x,y/2) * f(x,y/2) 			y % 2 == 0
f(x,y) = |  1								y == 0
 		 | f(x,y/2) * f(x,y/2) * x 		y % 2 == 1

输入格式:

第一行 整数t ∈[0,100]
接下来t行,每行有三个整数
x(1<= x <= 1e9)  y(1<= y <= 1e18)  p(1<= p <= le9)

输出 x^y%p 的值

样例输入:
1
2 10 10000

输出:
1024

*/
long long f_08(long long x,long long y,long long p) { //p位是不变的
	if( y == 0) {
		return 1 % p; //如果p=1 ,则为0 ,可能有坑,要加%p
	} else if(y % 2 == 0) {
		long long temp = f_08(x,y/2,p);
		return temp * temp % p;
	} else {
		long long temp = f_08(x,y/2,p);
		return temp * temp % p * x % p;  //不断 %p 防止溢出
	}
}

void test_08() {
	int t;
	scanf("%d",&t);
	long long x,y,p;
	while(t--) {
		scanf("%I64d%I64d%I64d",&x,&y,&p);
		printf("%I64d\n",f_08(x,y,p));
	}
}

/*
第一行输入一个n代表一共有n个弹簧板 n∈[1,200] ,每个板占一个单位距离
第二行输入n个数字,中间用空格隔开
第i个数字 让 a[i] ∈[0,30] 代表第i个板可以让小球移动的距离
第三行输入n个数字,中间用空格隔开
第i个数字b[i]∈[0,30],表示第i个弹簧板可以让小球移动的距离

输出一个数,表示小球被弹起的最小次数

输入:
5
2 2 3 1 4
1 2 3 4 1

输出:
2
*/

int a_09[205],b_09[205];
int ans_09[205];
bool vis_09[205];
int n_09;
int f_09(int x) {
	if(x >= n_09) {
		return ans_09[x];
	}
	if(vis_09[x]) {
		return ans_09[x];
	}
	vis_09[x] = true;
	return min(f_09(x + a_09[x]) , f_09(x + b_09[x])) + 1;
}

void test_09() {

	scanf("%d",&n_09);
	for(int i = 0; i < n_09; i++) {
		scanf("%d",&a_09[i]);
	}
	for(int i = 0; i < n_09; i++) {
		scanf("%d",&b_09[i]);
	}
	printf("%d\n",f_09(0));
}

/*最大公约数
输入t  (t行)
接下来t行 每行x y 求每行的最大公约数

样例输入:
1
6 8

输出:
2


*/
int gcd(int x,int y) {
	if(y == 0)
		return x;
	return gcd(y,x%y);   //最简写 ,不要用条件运算符
	/*
	}else{
		return gcd(y,x%y);
	}
	*/
}

void test_10() {
	int t;
	int x,y;
	scanf("%d",&t);
	while(t--) {
		scanf("%d%d",&x,&y);
		printf("%d",gcd(x,y));
	}
	return;
}

/*括号匹配
一个串 只包含 '(' 和 ')'
一对匹配的括号 :'(' 必须在 ')' 之前   (匹配的括号不能交叉,只能嵌套)
判断是否匹配 并输出匹配的一对括号的位置 (从1开始)

样例输入:
(())

输出:
Yes
1 4
2 3

*/
#include<cstdio>
#include<stack>
#include<cstring>  //strlen strcpy ...
char s_11[50005];  //括号字符串
stack<int> s_1;  //左括号入栈 ,遇到右括号栈非空,(有左括号)出栈
int ans[50005]; // 记录位置
void test_11() {
	int len;
	bool f = true;
	scanf("%s",s_11);
	len = strlen(s_11);
	for(int i = 0; i < len; i++) {
		if(s_11[i] == '(') {
			s_1.push(i + 1);
		} else {
			if(!s_1.empty() ) { //还有左括号
				ans[i + 1] = s_1.top();
				s_1.pop();
			} else {  //未匹配完,栈已经空了(左括号不够)== 右括号多了
				f = false;
				break;
			}
		}
	}
	if(!s_1.empty()) { //匹配完,栈内还有左括号,左括号多了
		f = false;
	}
	if(!f) { //没有匹配成功
		printf("No\n");
	} else {
		printf("Yes\n");
		for(int i = 1; i <= len; i++) {  //输出i位置括号对应的括号 (位置从1开始)
			if(ans[i]) {
				printf("%d %d\n",ans[i],i);
			}
		}
	}
}

/*浏览器

三种操作 : 打开页面 回退 前进

打开页面 : 在地址栏中输入网址,并跳转到网址对应的对面
回退:回到上一次访问到的页面
前进:返回到上次回退的页面,如果上一次的操作是打开页面,那么将无法前进

头文件
#include<iostream>
#include<stack>

输入n行,表示操作次数

如果有VISIT,后面接着输入一个不含有空格和换行的网址(长度< 100)
表示在浏览器中输入的网址,如果是BACK , 表示回退,FORWARD 表示前进

输出格式
操作成功输出操作后的网址,否则输出Ignore

样例输入:
10
VISIT http://www.jisuanke.com/course/476
VISIT http://www.taobao.com/
BACK
BACK
FORWARD
FORWARD
BACK
VISIT http://www.jisuanke.com/course/429
FORWARD
BACK

输出:
http://www.jisuanke.com/course/476
http://www.taobao.com/
http://www.jisuanke.com/course/476
Ignore
http://www.taobao.com/
Ignore
http://www.jisuanke.com/course/476
http://www.jisuanke.com/course/429
Ignore
http://www.jisuanke.com/course/476


*/
#include<queue>
#include<string>  //要输入完再输出就多一个queue q存放每轮结果存放每轮 
queue<string> q;
string op,s; //op 操作 s为输入网址
stack<string> s1, s2; //s1输出当前网址   ,  s2 临时存放回退前的网址
void test_12() {
	int n;
	cin >> n;
	for(int i = 0; i < n; i++) {
		cin >> op;
		if(op == "VISIT") { //表示输入网址
			cin >> s;
			while(!s2.empty()) { //等效清空! (栈无clear)    !!!!
				s2.pop();
			}
			s1.push(s); //网址放入s1
		} else if(op == "BACK") {
			if(s1.size() <= 1) {  //回退到 至少剩下一个首页  ,初始界面
				cout<< "Ignore" <<endl;
		//		q.push("Ignore");
				continue;
			}
			s2.push(s1.top()); //存回退前网址(下一次前进网址)
			s1.pop(); //出栈
		} else {//前进
			if(s2.empty()) { //没有可以前进的网址
				cout<< "Ignore" <<endl;
			//	q.push("Ignore");
				continue;
			}
			s1.push(s2.top()); //取s2存放的 回退前网址
			s2.pop(); //清 临时存放网址
		}
		cout<< s1.top() << endl; //没有被忽略就打印 (continue会跳过此步)
	//	q.push(s1.top());
	}
	/*
	while(!q.empty()){
		q.pop();
	}
	*/
}



int main() {
	test_12();
	return 0;
}





汉诺塔

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值