蓝桥杯省赛训练营——栈与递归

今天博主复习了一下栈与递归的知识,做了计蒜客平台的一章习题。下面贴出来和大家交流分享。如有不正之处,请求指教。

蒜头君吃桃

题目描述蓝桥杯省赛训练营——栈与递归_第1张图片

思路分析

设第i天还剩下g(n)个桃子 那么第i-1天还剩下g(n-1)个桃子。

根据递推关系 有:g(n-1) = g(n) - g(n)/2 - 1 所以 g(n) = 2(g(n-1) + 1)

第一天还剩下1个桃子 所以g(1) = 1

那么可以写出递归函数:

int64_t GetNumber(int n){
	if(n==1) return 1;
	else return 2*(GetNumber(n-1)+1);
}
int main(){
	int n;cin>>n;
	cout<<GetNumber(n)<<endl;
	return 0;
}

斐波那契数列?

题目描述

蓝桥杯省赛训练营——栈与递归_第2张图片

思路分析

这一题想都不用想 是最简单的矩阵快速幂模板题。

重点是求出转移矩阵。

Matix K = [f(2),f(1)]T(转置矩阵) T为转移矩阵

那么T(n-2)*K = [f(n), f(n-1)]T

Matix T = {
	a,b,
	1,0
};

所以可以通过快速幂求解

代码

const int N = 2;
int n,a,b,p;
typedef struct Matix{  //定义N维方阵
	int64_t m[N][N];
}Matix;
Matix temp;//矩阵乘法temp = a*b
Matix Matix_Mutiply(Matix a,Matix b){
	memset(temp.m,0,sizeof(temp.m));
	for(int i=0;i<N;i++){
		for(int j=0;j<N;j++)
			for(int k=0;k<N;k++)
			temp.m[i][j] = (temp.m[i][j] + a.m[i][k]*b.m[k][j]%p)%p;
	}
	return temp;
}
Matix ans,c;//c为单位阵 ans来代替a
//矩阵快速幂 c = a^n^
Matix Matix_pow(Matix a,int n){
	memset(c.m,0,sizeof(c.m));
	for(int i=0;i<N;i++) c.m[i][i] = 1;
	ans = a;
	while(n){
		if(n&1)  c = Matix_Mutiply(c,ans);
		ans = Matix_Mutiply(ans,ans);
		n = n>>1;
	}
	return c;
}
Matix K = {   //初始的方阵
	1,0,
	1,0
};
int main(){
	cin>>n>>a>>b>>p;
	if(n==1||n==2){  //如果求的是第一项或者第二项
		cout<<1<<endl;
		return 0;
	}
	Matix T = {  //转移矩阵,也叫变换矩阵T
		a,b,
		1,0
	};
	T = Matix_pow(T,n-2);
	cout<<T.m[0][0]+T.m[0][1]<<endl;//f(n) = 矩阵T的第一行乘以矩阵K的第一列
	return 0;
}

快速幂

题目描述

蓝桥杯省赛训练营——栈与递归_第3张图片

思路分析

这是整数快速幂的裸题,就不用多说了。

代码

int64_t fastpow(int64_t x,int64_t n,int64_t p){
	int64_t ans = 1;//赋初值
	if(n==0) return ans;
	while(n){
		if(n&1) ans = (ans*x)%p;//mod
		x = (x*x)%p;//mod
		n = n>>1;
	}
	return ans;
}
int main(){
	int t;cin>>t;
	int64_t x,y,p;
	while(t--){
		cin>>x>>y>>p;
		cout<<fastpow(x,y,p)<<endl;
	}
	return 0;
}

弹簧板

题目描述

蓝桥杯省赛训练营——栈与递归_第4张图片

思路分析

很显然,这个最简单的方法是递归,因为n<=200 不用担心栈溢出问题。

那么递归怎么写呢?

设函数function(int i,int n)表示小球此时在第i个弹簧板上 一共有n个弹簧板的最小移动距离

那么function(i,n) = min(function(i+a[i],n), function(i+b[i],n)) + 1

而且当i>n的时候 function(i,n) = 0;

int a[205],b[205];
int Getmin(int t,int n){
	if(t>n) return 0;
	return min(Getmin(t+a[t],n),Getmin(t+b[t],n)) + 1;
}

代码

int a[205],b[205];
int Getmin(int t,int n){
	if(t>n) return 0;
	return min(Getmin(t+a[t],n),Getmin(t+b[t],n)) + 1;
}
int main(){
	int n;cin>>n;

	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) cin>>b[i];

	cout<<Getmin(1,n)<<endl;
	return 0;
}

最大公约数

题目描述

蓝桥杯省赛训练营——栈与递归_第5张图片

思路分析

啥也别说了 默写gcd()函数、

代码

int gcm(int a,int b){
	int p = max(a,b);
	int q = min(a,b);
	int temp;
	while(q){
		temp = p%q;
		p = q;
		q = temp;
	}
	return p;
}
int main(){
	int t;cin>>t;
	int x,y;
	while(t--){
		cin>>x>>y;
		cout<<gcm(x,y)<<endl;
	}
	return 0;
}

括号匹配

题目描述

蓝桥杯省赛训练营——栈与递归_第6张图片

思路分析

这是一道常规的进栈 出栈的题目。难点在于如何对括号进行匹配的保存问题,我采用map的形式保存。

对于一个括号字符串 输入之后首先判断他的长度是否为奇数 如果是奇数 一定不合法匹配

顺序扫描这个字符串:

1.如果扫描到是左括号 那么这个位置的i入栈
2.如果扫描到右括号,左括号序列出栈
2.1如果左括号序列为空 那么不合法
2.2出栈一个元素 同时map[i] = j;
3.扫描完毕之后 如果栈非空 那么不合法
4.只有执行完以上所有条件 这样的括号字符串才合法 输出Yes 同时遍历输出map

代码

#include
#include
string s;//输入的括号串
int main(){
	stack<int>S;map<int,int>mym;//map表示两个位置的括号形成匹配
	cin>>s;
	if(s.length()%2!=0){  //如果字符串长度为奇数 肯定不匹配
		cout<<"No"<<endl;return 0;
	}
	for(int i=0;i<s.length();i++){
		if(s[i] == '(') S.push(i);
		else{
			if(S.empty()) {cout<<"No"<<endl;return 0;} //说明匹配错误
			else {
				mym[S.top()+1] = i+1;//字符串下标i是从0开始遍历的
				S.pop();
			}
		}
	}
	if(!S.empty()){cout<<"No"<<endl;return 0;}
	//接下来的才是正确的判断
	cout<<"Yes"<<endl;
	for(map<int,int>::iterator it = mym.begin();it!=mym.end();it++)
		cout<<(it->first)<<" "<<(it->second)<<endl;
	return 0;
}

网页跳转

题目描述

蓝桥杯省赛训练营——栈与递归_第7张图片

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

思路分析

博主看到这个题,第一反应是双栈问题,然后将打开,后退,前进看成是不同的进栈,出栈操作。

这个题的难点在于 对于每一个打开的操作 要清空此时的F栈。

可能大家不知道F栈是什么定义,下面我写一下我的解题思路。

思路说明:将进入网页 回退 前进看成进栈 出栈 问题

  1. 定义双栈 前进栈F 回退栈B 便于理解与操作;
  2. 当操作为进入p时 一定是合法的输入 此时应该B.push(p) 清空栈F(因为这是一次新的浏览 之前的Forward记录都被淹没了) ,同时输出B.top();
  3. 当操作为回退时,要先判断栈B的元素个数是否大于1 如果大于1说明可以回退,如果不大于1说明不能回退
    3.1如果不能回退 直接输出Ignore
    3.2如果可以回退 B栈的栈顶元素p出栈 B.pop() 同时F.push(p) 输出B.top()
  4. 当操作是前进时 判断F栈是否为空 如果为空说明不能前进 不为空说明可以前进
    4.1如果不能前进 直接输出Ignore
    4.2如果可以前进 F栈的栈顶元素p出栈 F.pop(),同时B.push(p) 输出B.top()

代码

//思路说明:将进入网页 回退 前进看成进栈 出栈 问题
//1.定义双栈 便于理解与操作
//2.当操作为进入p时 一定是合法的输入 此时应该B.push(p) 清空栈F(因为这是一次新的浏览 之前的记录都被淹没了)同时输出B.top();
//3.当操作为回退时,要先判断栈B的元素个数是否大于1 如果大于1说明可以回退,如果不大于1说明不能回退
//  3.1如果不能回退 直接输出Ignore
//  3.2如果可以回退 B栈的栈顶元素p出栈 B.pop() 同时F.push(p) 输出B.top()
//4.当操作是前进时 判断F栈是否为空 如果为空说明不能前进 不为空说明可以前进
//  4.1如果不能前进 直接输出Ignore
//  4.2如果可以前进 F栈的栈顶元素p出栈 F.pop(),同时B.push(p) 输出B.top()
int main(){
	ios::sync_with_stdio(false);//加快cin cout输入输出流
	string p,opt;//opt为操作码 p为栈进行交换的码
	stack<string>B;stack<string>F;//栈B为back栈  栈F为FORWARD栈
	int n;cin>>n;
	for(int i=1;i<=n;i++){
		cin>>opt;
		if(opt == "VISIT") {
			cin>>s;
			while(!F.empty()) F.pop();//清空F栈
			B.push(s);
			cout<<s<<endl;//打印back栈的栈顶元素
		}else if(opt == "BACK"){
			if(B.size()<=1){
				//说明不能回退
				cout<<"Ignore"<<endl;
			}else{//可以回退的情况下
				p = B.top();B.pop(); //back栈的栈顶元素出栈并进入Forward栈
				F.push(p);
				cout<<B.top()<<endl;//打印back栈的栈顶元素
			}
		}else if(opt == "FORWARD"){
			if(F.empty()){ //不可以前进
				cout<<"Ignore"<<endl;
			}else{ //可以前进的情况下
				 p = F.top();F.pop();//forward栈的栈顶元素出栈进入back栈
				 B.push(p);
				 cout<<B.top()<<endl;//打印back栈的栈顶元素
			}
		}
	}
	return 0;
}

你可能感兴趣的:(算法设计)