算法---递归(1)

目录

一、小q的数列

二、求先序排列

 三、FBI树

四、表达式计算 


一、小q的数列

题目链接:小q的数列

题目描述

小q最近迷上了各种好玩的数列,这天,他发现了一个有趣的数列,其递推公式如下:

f[0]=0 f[1]=1;
f[i]=f[i/2]+f[i%2];(i>=2)

现在,他想考考你,问:给你一个n,代表数列的第n项,你能不能马上说出f[n]的值是多少,以及f[n]所代表的值第一次出现在数列的哪一项中?(这里的意思是:可以发现这个数列里某几项的值是可能相等的,则存在这样一个关系f[n'] = f[n] = f[x/2]+f[x%2] = f[x]...(n'<n<x) 他们的值都相等,这里需要你输出最小的那个n'的值)(n<10^18)

输入描述:

输入第一行一个t
随后t行,每行一个数n,代表你需要求数列的第n项,和相应的n'
(t<4*10^5)

输出描述:

输出每行两个正整数
f[n]和n',以空格分隔

示例1

输入

复制2 0 1

2
0
1

输出

复制0 0 1 1

0 0
1 1

个人题解:f[n]=f[n/2]+f[n%2],举一个例子,比如说f[66].

f[66]=f[33]+f[0]

      =f[16]+f[1]=f[16]+1

      =f[8]+f[0]+1

      =f[4]+f[0]+1

      =f[2]+f[0]+1

     =f[1]+1=1+1

这里的f[n]仔细看看其实也就是n的二进制形式中1的个数,为什么这么说呢,每一次除以二,其实就是右移一位,然后n%2,也就是看看最右边数是1还是0,那么这不就是求1的个数吗

至于求最早出现的相同值的n,只要你求出了之前1的个数,那么这个数其实就是连着1的个数,也就是2的1的个数的次方-1.换成二进制表达就是1左移1的个数位-1(1是long long类型)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n;
int t;
ll f(ll x)
{
	//二进制下有几个1
	ll sum=0;
	while(x){
		if(x&1==1){
			sum++;
		}
		x=x>>1;
	}
	return sum;
}
ll zuixiao(ll x)
{
	return ((1LL<<x)-1);
}
int main()
{
	scanf("%d",&t);
	while(t--){
		scanf("%ld",&n);
		ll x=f(n);
		printf("%ld %ld\n",x,zuixiao(x));
	}
	return 0;
}

二、求先序排列

题目链接:求先序排列

题目描述

给出一棵二叉树的中序与后序排列。求出它的先序排列。(约定树结点用不同的大写字母表示,长度 ≤ 8)。

输入描述:

2行,均为大写字母组成的字符串,表示一棵二叉树的中序与后序排列。

输出描述:

1行,表示一棵二叉树的先序。

示例1

输入

复制BADC BDCA

BADC
BDCA

输出

复制ABCD

ABCD

 个人题解:知道中序排列和后序排列以及中序排列和先序排列,都是可以求出这棵二叉树的也就可以求出对应的排列。但是知道先序排列和后序排列的情况下是无法求出中序排列的。

为什么呢?因为中序排列是左根右,而先序排列从前往后可以知道根,后序排列重后往前可以知道根,对应的中序排列以根为界限即可知道左子树和右子树。

来看看本题,知道了中序排列和后序排列,该如何做,代码有解释。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
string zhong,hou;

void deal(int l1,int r1,int l2,int r2)
{
	//l1,r1其实就是中序排列的从l1到r1的一颗子树
	//l2,r2是后序排列
	if(l1>r1)return ;
	char root=hou[r2];
	cout<<root;
	int pos=-1;//记录根的位置
	for(int i=l1;i<=r1;i++){
		if(root==zhong[i]){
			pos=i;
			break;
		}
	}
	//递归左子树
	deal(l1,pos-1,l2,l2+pos-1-l1);
	//递归右子树
	deal(pos+1,r1,r2-r1+pos,r2-1);
	//这里有个技巧中序排列和后序排列他们的子树长度是一样的
	//不会求端点可以列方程求出
}

int main()
{
	cin>>zhong>>hou;
	int l=zhong.size();
	deal(0,l-1,0,l-1);
}

 三、FBI树

题目链接:FBI树

题目描述

我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全“1”串称为I串,既含“0”又含“1”的串则称为F串。

FBI树是一种二叉树[1],它的结点类型也包括F结点,B结点和I结点三种。由一个长度为2N的“01”串S可以构造出一棵FBI树T,递归的构造方法如下:

1) T的根结点为R,其类型与串S的类型相同;

2) 若串S的长度大于1,将串S从中间分开,分为等长的左右子串S1S2;由左子串S1构造R的左子树T1,由右子串S2构造R的右子树T2

现在给定一个长度为2N的“01”串,请用上述构造方法构造出一棵FBI树,并输出它的后序遍历[2]序列。

[1] 二叉树:二叉树是结点的有限集合,这个集合或为空集,或由一个根结点和两棵不相交的二叉树组成。这两棵不相交的二叉树分别称为这个根结点的左子树和右子树。

[2] 后序遍历:后序遍历是深度优先遍历二叉树的一种方法,它的递归定义是:先后序遍历左子树,再后序遍历右子树,最后访问根。

输入描述:

 
 

第一行是一个整数N(0 <= N <= 10)

第二行是一个长度为2N的“01”串。

输出描述:

一个字符串,即FBI树的后序遍历序列。

示例1

输入

复制3 10001011

3
10001011

输出

复制IBFBBBFIBFIIIFF

IBFBBBFIBFIIIFF

备注:

对于40%的数据,N <= 2;
对于全部的数据,N<= 10。

个人题解:本题题意大概如下:

10001011

从中间分,如:

                                          10001011

                                1000               1011

                           10   00               10     11

                         1  0  0  0             1  0   1  1

全是1是I串,全是0则为B串,有0有1是F串

本题可以通过递归来实现,具体如下

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
char s[1200];

char fbi(int l,int r)
{
	if(l==r)//左点等于右点只有一个点
	{
		//只有一个点
		if(s[l]=='0'){
			cout<<'B';
			return 'B';
		}
		else{
			cout<<'I';
			return 'I';
		}
	}
	//
	int mid=(l+r)/2;
	char a=fbi(l,mid);//先递归左子树
	char b=fbi(mid+1,r);//再递归右子树
    //左子树和右子树递归完,开始查看根节点
    //而根节点的查看是通过左子树和右子树来比较的
	if(b=='B'&&a=='B'){
		//全是0
		cout<<'B';
		return 'B';
	}
	else if(b=='I'&&a=='I')
	{
		cout<<'I';
		return 'I';
	}
	cout<<'F';
	return 'F';
}

int main()
{
	scanf("%d",&n);
	scanf("%s",&s);
	int l=strlen(s);
	fbi(0,pow(2,n)-1);
	return 0;
}

四、表达式计算 

题目链接:表达式计算

题目描述

给出一个表达式,其中运算符仅包含+,-,*,/,^(加 减 乘 整除 乘方)要求求出表达式的最终值

数据可能会出现括号情况,还有可能出现多余括号情况

数据保证不会出现≥231的答案数据保证不会出现\geq 2^{31}的答案数据保证不会出现≥231的答案

数据可能会出现负数情况

输入描述:

仅一行,即为表达式

输出描述:

仅一行,既为表达式算出的结果

示例1

输入

复制(2+2)^(1+1)

(2+2)^(1+1)

输出

复制16

16

备注:

表达式总长度≤30表达式总长度 \leq 30表达式总长度≤30

个人题解:没什么好理解的。可以用栈来写。但这里我用的是递归来写。具体看代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

//表达式求值 
string s;
int shu(int l,int r)
{
	int num=0;
	for(int i=l;i<=r;i++){
		num=num*10+(s[i]-'0');
	}
	return num;
}
int cal(int l,int r)
{
	int cnt=0;
	int pos1=-1,pos2=-1,pos3=-1;//分别记录加减,乘除,和幂次方的最后一次出现的位置
	for(int i=l;i<=r;i++){
		if(s[i]=='(')cnt++;
		if(s[i]==')')cnt--;
		if(cnt<=0){
			if(s[i]=='+'||s[i]=='-')pos1=i;
			if(s[i]=='*'||s[i]=='/')pos2=i;
			if(s[i]=='^')pos3=i;
		}
	}
	if(pos1==-1&&pos2==-1&&pos3==-1){
		if(cnt==0&&s[l]=='('){
			return cal(l+1,r-1);
		}
		if(cnt>0&&s[l]=='('){
			return cal(l+1,r);
		}
		if(cnt<0&&s[r]==')'){
			return cal(l,r-1);
		}
		return shu(l,r);
	}
	if(pos1!=-1){
		//
		if(s[pos1]=='+'){
			return cal(l,pos1-1)+cal(pos1+1,r);
		}
		else{
			return cal(l,pos1-1)-cal(pos1+1,r);
		}
	}
	else if(pos2!=-1){
		if(s[pos2]=='*'){
			return cal(l,pos2-1) * cal(pos2+1,r);
		}
		else{
			return cal(l,pos2-1) / cal(pos2+1,r);
		}
	}
	else{
		if(s[pos3]=='^')return pow(cal(l,pos3-1),cal(pos3+1,r));
	}
    return 0;
}

int main()
{
	cin>>s;
	int l=s.size();
	cout<<cal(0,l-1);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/gaoqiandr/article/details/128853029