计蒜客 -- 栈和递归题解

栈(stack):一种特殊线性数据结构
只允许在栈的一端插入或删除元素【栈顶】,另一端【栈底
重要性质:先进后出,类似于往子弹夹装子弹然后发弹

递归:函数调用自身,一般解决有重复子问题的问题。
边界条件:什么情况下,函数不应该再继续调用自身

1.计蒜客 – 汉诺塔2

汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着 64
片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

现在蒜头君开始玩汉诺塔游戏, 他放了 n 片黄金圆盘在第一根柱子上,从上到下依次编号为 1~n, 1 号圆盘最小,n号圆盘最大。蒜头君移动第 i 号圆盘的时候需要花费 i 点体力。现在蒜头君想把圆盘全部移动到第 2根柱子上,移动过程中蒜头君必须准守游戏规则。

现在蒜头君想知道他完成游戏的最小移动次数和最少消耗的体力。

输入格式

输入一个正整数 n(1≤n≤60) 表示黄金圆盘的个数

输出格式

一行输出 2 个数,表示最小移动次数和最小消耗的体力,中间用一个空格隔开。

输入样例1

2

输出样例1

3 4

输入样例2

3

输出样例2

7 11

题解:
移动次数递推式:
只有一个盘子时的步数f[1] = 1;
n个盘子时的步数f[n] = f[n-1] + 1 + f[n-1];
根据高中数学等比数列可推出==>f[n] = pow(2 , n) - 1;
//pow不要用,算大数易超时
同理可得
消耗体力递推式:
n个盘子时消耗的体力g[n] = g[n-1] + n + g[n-1];

代码如下

#include<bits/stdc++.h>
using namespace std;
long long f[65],g[65];
int main()
{
	int n;
	cin >> 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;
	}
	cout << f[n] << " " << g[n];
	return 0;
}

2.计蒜客 – 蒜头君吃桃

蒜头君买了一堆桃子不知道个数,第一天吃了一半的桃子,还不过瘾,有多吃了一个。以后他每天吃剩下的桃子的一半还多一个,到 n天只剩下一个桃子了。蒜头君想知道一开始买了多少桃子。

输入格式

输入一个整数 n(2≤n≤60),代表第 n天剩了一个桃子。

输出格式

输出买的桃子的数量。

样例输入1

2

样例输出1

4

样例输入2

3

样例输出2

10

见到这道题的第一反应是:经典题猴子吃桃改编,所以 蒜头君 的本体其实是只猴子🤔
代码如下

#include<bits/stdc++.h>
using namespace std;
int n;
int f(int x)
{
	if(x==n){
		return 1;  //x到了第n天(也就是最后一天),他就只有一个桃子 
	}else{
		return (f(x+1) + 1)*2;  
		/*跟我下一天剩多少桃子有关,假如某天剩m个,那么接下来一天剩(m/2-1)个,
		反推,如果已知后一天那么前一天剩(f(x+1)+1)*2*/
	}
}
int main()
{
	cin >> n;
	cout << f(1);  //求的是第一天吃了多少桃子 
	return 0;
}

3.计蒜客 – 快速幂
题目描述:

x^y相信大家都会计算(for循环相乘),但是当y很大的时候怎么办?(如:y=1e18)
蒜头君经研究发现,想到一个好办法,公式如下
f(x,y) = f(x,y/2)*f(x,y/2) ----------- y%2==0 && y>0

f(x,y) =1 ------------------------------ y=0

f(x,y) = f(x,y/2)*f(x,y/2) *x -------- y%2==1 && y>0

当然这不是快速幂最好的写法,有兴趣的同学可以了解一个快速幂的另一种写法

输入格式

第一行输入一个整数t(t<=100)。然后会有t行,每行有三个整数x(1≤n≤1e9),y(1≤y≤1e18),p(1≤n≤1e9)。

输出格式

输出x^y%p的值。

样例输入

1
2 10 10000

样例输出

1024

代码如下:

#include<bits/stdc++.h>
using namespace std;
#define LL long long 
LL f(LL x,LL y,LL p)
{
	if(y==0){
		return 1 % p; //不能直接return1,p未知,p为1就凉凉 
	}else if(y%2==0){
		LL temp = f(x,y/2,p);
		return temp*temp%p;
	}else{
		LL temp = f(x,y/2,p);
		return temp*temp%p*x%p;  //*一下%一下是因为哪哪都可能炸 
	}
}
int main()
{
	int t; 
	LL x,y,p;
	cin >> t;
	while(t--){
		cin >> x >> y >> p;
		cout << f(x,y,p);
	}
	return 0;
}

4.计蒜客 – 弹簧板
在这里插入图片描述
题目补充:或者跳到b[i],第三行要继续输入一行b[i]
样例输入

5
2 2 3 1 2
1 2 3 4 1

样例输出

2

代码如下

#include<bits/stdc++.h>
using namespace std;
int a[205],b[205];
int ans[205];  //防止重复访问,记录一下 
bool vis[205];  //默认值false 
int n;
int f(int x)
{
	if(x>=n) return 0;  //防止vis访问不进 
	if(vis[x]){
		return ans[x];
	} 
	vis[x] = true;
	return ans[x] = min(f(x+a[x]) , f(x+b[x])) + 1;
}
int main()
{
	cin >> n;
	for(int i=0; i<n; i++){
		cin >> a[i];
	}
	for(int i=0; i<n; i++){
		cin >> b[i];
	}
	cout << f(0);
	return 0;
}

5.计蒜客 – 最大公约数
快速方法:辗转相除法

int gcd(int x,int y)
{
	return !y ? x : gcd(y,x%y);
	/*等价于
	if(y==0){
		return x;
	}else{
		return gcd(y,x%y);
	}*/
}

6.计蒜客 – 快速匹配
问题描述

蒜头君在纸上写了一个串,只包含’(‘和’)’。一个’(‘能唯一匹配一个’)’,但是一个匹配的’(‘必须出现在’)’之前。请判断蒜头君写的字符串能否括号完全匹配,如果能,输出配对的括号的位置(匹配的括号不可以交叉,只能嵌套)。

输入格式

一行输入一个字符串只含有’(‘和’)’,输入的字符串长度不大于50000。

输出格式

如果输入括号不能匹配,输出一行”No”,否则输出一行”Yes”,接下里若干行每行输出 2个整数,用空格隔开,表示所有匹配对的括号的位置(下标从 1 开始)。你可以按照任意顺序输出。 本题答案不唯一,符合要求的答案均正确

样例输入

()()

样例输出

Yes
1 2
3 4

某大佬括号匹配算法思路,强推!!!
代码如下

#include<bits/stdc++.h>
using namespace std;
string str;
stack<int> s;
int ans[50005];
int main()
{
	bool f = true;
	cin >> str;
	for(int i=0;i<str.size();i++){
		if(str[i]=='('){
			s.push(i+1);
		}else{
			if(!s.empty()){
				ans[i+1] = s.top();
				s.pop();
			}else{
				f = false;
				break;
			}
		}
	}
	if(!s.empty()){
		f = false;
	}
	if(!f){
		cout << "No" << endl;
	}else{
		cout << "Yes" << endl;
		for(int i=1; i<=str.size(); i++){
			if(ans[i]){
				cout << ans[i] << " " << i << endl; 
			}
		}
	}
	return 0;
}

7.计蒜客 – 网页跳转
题目要求:

蒜头君每天都在用一款名为“蒜厂浏览器”的软件。在这个浏览器中,一共三种操作:打开页面、回退和前进。它们的功能如下:

打开页面:在地址栏中输入网址,并跳转到网址对应的页面;
回退:返回到上一次访问的页面;
前进:返回到上次回退前的页面,如果上一次操作是打开页面,那么将无法前进。
现在,蒜头君打开浏览器,进行了一系列操作,你需要输出他每次操作后所在页面的网址。

输入格式

第一行输入一个整数 n(0 < n \le 100000)n(0<n≤100000),表示蒜头君的操作次数。

接下来一共 n 行,每行首先输入一个字符串,如果是VISIT,后面接着输入一个不含有空格和换行的网址(网址长度小于100100),表示蒜头君在浏览器地址栏中输入的网址;如果是BACK,表示蒜头君点击了回退按钮;如果是FORWARD,表示蒜头君点击了前进按钮。

输出格式

对于每次操作,如果蒜头君能操作成功,输出蒜头君操作之后的网址,否则输出Ignore。假设蒜头君输入的所有网址都是合法的。

样例输入

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

代码如下

#include<bits/stdc++.h>
using namespace std;
stack<string> s1,s2;
string op,s;
int main()
{
	int n;
	cin >> n;
	for(int i=0; i<n; i++){
		cin >> op;
		if(op[0]=='V'){
			cin >> s;
			while(!s2.empty()){
				s2.pop();
			}
			s1.push(s);
		}else if(op == "BACK"){
			if(s1.size()<=1){
				cout << "Ignore" << endl;
				continue;
			}
			s2.push(s1.top());
			s1.pop();
		} else{
			if(s2.empty()){
				cout << "Ignore" << endl;
				continue;
			}
			s1.push(s2.top());
			s2.pop();
		}
		cout << s1.top() << endl;
	}
	return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值