今天博主复习了一下栈与递归的知识,做了计蒜客平台的一章习题。下面贴出来和大家交流分享。如有不正之处,请求指教。
蒜头君吃桃
题目描述![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/75604951ce55ac380aee63525e482882.png)
思路分析
设第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;
}
斐波那契数列?
题目描述
思路分析
这一题想都不用想 是最简单的矩阵快速幂模板题。
重点是求出转移矩阵。
设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;
}
快速幂
题目描述
思路分析
这是整数快速幂的裸题,就不用多说了。
代码
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;
}
弹簧板
题目描述
思路分析
很显然,这个最简单的方法是递归,因为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;
}
最大公约数
题目描述
思路分析
啥也别说了 默写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;
}
括号匹配
题目描述
思路分析
这是一道常规的进栈 出栈的题目。难点在于如何对括号进行匹配的保存问题,我采用map的形式保存。
对于一个括号字符串 输入之后首先判断他的长度是否为奇数 如果是奇数 一定不合法匹配
顺序扫描这个字符串:
1.如果扫描到是左括号 那么这个位置的i入栈
2.如果扫描到右括号,左括号序列出栈
2.1如果左括号序列为空 那么不合法
2.2出栈一个元素 同时map[i] = j;
3.扫描完毕之后 如果栈非空 那么不合法
4.只有执行完以上所有条件 这样的括号字符串才合法 输出Yes 同时遍历输出map
代码
#include<stack>
#include<map>
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;
}
网页跳转
题目描述
样例输入:
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栈是什么定义,下面我写一下我的解题思路。
思路说明:将进入网页 回退 前进看成进栈 出栈 问题
- 定义双栈 前进栈F 回退栈B 便于理解与操作;
- 当操作为进入p时 一定是合法的输入 此时应该B.push(p) 清空栈F(因为这是一次新的浏览 之前的Forward记录都被淹没了) ,同时输出B.top();
- 当操作为回退时,要先判断栈B的元素个数是否大于1 如果大于1说明可以回退,如果不大于1说明不能回退
3.1如果不能回退 直接输出Ignore
3.2如果可以回退 B栈的栈顶元素p出栈 B.pop() 同时F.push(p) 输出B.top()- 当操作是前进时 判断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;
}