强行刷段位第四天

今天上来就是一道通过二元数组建立二叉树然后DFS和BFS的题。

忘了不说。。。也有点复杂。。。

看来今天是刷不完递归了


递归第一题:

题目:

给出一个二叉树,输出它的最大宽度和高度。

输入描述:

第一行一个整数n。

下面n行每行有两个数,对于第i行的两个数,代表编号为i的节点所连接的两个左右儿子的编号。如果没有某个儿子为空,则为0。

输出描述:

输出共一行,输出二叉树的最大宽度和高度,用一个空格隔开。

样例输入:

5

2 3

4 5

0 0

0 0

0 0

样例输出:

2 3

数据范围及提示:

n<16

默认第一个是根节点

以输入的次序为编号

2-N+1行指的是这个节点的左孩子和右孩子

注意:第二题有极端数据!

          1

          0 0

这题你们别想投机取巧了,给我老老实实搜索!

我的答案:

。。。这道题的提示有点硬核啊。。。(笑哭)

得,那就按部就班DFS和BFS吧

大体思想是明白的,一个递归,一个出入队。

开始想写链表。。。无奈已经忘记了,太难写。

后来按照别人的,磕磕绊绊,定义了一个结构体,然后申请了一个数组出来。

一些语法要点要注意。

另外,从本题中要学会运用queue的。.empty()、.size()等函数。

这题掌握的不够扎实,知道个大概,细节却不会实现,这么经典的算法,一定要好好掌握。

今天状态不好,心不静,要注意调整。至少还得再做一题。

本题如下:

#include <iostream>
#include <queue>
using namespace std;

struct node  //二叉树结构体 
{
    int lchild;
	int rchild;
};

int dfs(node *arr,int n)  //深度优先前序递归 
{
	int d=0;
	if(n>0)
	{
	int ld=0,rd=0;
    ld=dfs(arr,arr[n].lchild);
    rd=dfs(arr,arr[n].rchild);
    d=(ld>rd)?ld+1:rd+1;
	}
    return d;
}

int bfs(node *arr)
{
	int w;
	queue<node> t;
	int i=0;
	int temp=1;
	t.push(arr[1]);
	while(!t.empty())
	{
		int i=0;
		temp=t.size();
		while(i<temp)
		{
			if(t.front().lchild!=0)  t.push(arr[t.front().lchild]);
			if(t.front().rchild!=0)  t.push(arr[t.front().rchild]);
			t.pop();
			i++;
		}
		if(temp>w) w=temp;
	}
	return w;
}

int main()
{
	int n=0,i=0,d=0,w=0;
    cin>>n;
    node *arr=new node[n+1];
    for(i=1;i<=n;i++)
    {
    	cin>>arr[i].lchild>>arr[i].rchild;
	}
	
	w=bfs(arr);
    d=dfs(arr,1);
    
    cout<<w<<" "<<d<<endl;
	
	return 0;

 } 

递归第二题:

题目:

同学们在做题时常遇到这种函数

f(x)=5 (x>=0)

f(x)=f(x+1)+f(x+2)+1 (x<0)

下面就以这个函数为题做一个递归程序吧

输入描述:

一个数表示f(x)中x值

大家注意就一个数,前面代表样例编号

输出描述:

一个数表示值

大家注意就一个数,前面代表样例编号

样例输入:

样例一:0

样例二:-5

样例输出:

样例一:5

样例二:77

数据范围及提示:

x>=-30

我的答案:

恕我直言,这道题真的好简单。。。不像是做题,像是做外文翻译。

属于打一巴掌之后的甜枣。

这也告诉我不能总是急吼吼的。当下的题难,也许以后的题简单。

平心静气,不要总是着急。。。

要平静,要冷静,要留得青山在,要泰山崩于前而色不变。

代码:

#include<iostream>
using namespace std;

int f(int x)
{
	int r=0;
	if(x>=0) r= 5;
	else r=f(x+1)+f(x+2)+1;
	return r;
}

int main() 
{
	int n=0,result=0;
	cin>>n;
	
	result=f(n);
	cout<<result;
	
	return 0;
}

加油哇!


果然下午手痒,又来刷题了~


递归第三题:

题目:

3n+1问题是一个简单有趣而又没有解决的数学问题。这个问题是由L. Collatz在1937年提出的。克拉兹问题(Collatz problem)也被叫做hailstone问题、3n+1问题、Hasse算法问题、Kakutani算法问题、Thwaites猜想或者Ulam问题。

问题如下:

(1)输入一个正整数n;

(2)如果n=1则结束;

(3)如果n是奇数,则n变为3n+1,否则n变为n/2;

(4)转入第(2)步。

克拉兹问题的特殊之处在于:尽管很容易将这个问题讲清楚,但直到今天仍不能保证这个问题的算法对所有可能的输入都有效——即至今没有人证明对所有的正整数该过程都终止。

输入描述:

第一行是一个整数T.表示输入数据的组数.

第二行是T个正整数n.

输出描述:

对于每个正整数n,每行输出一个数s,表示n通过多少步变换会变成1,如果n无法变成1,则输出-1.

样例输入:

3

1 2 3

样例输出:

0

1

7

数据范围及提示:

1 <= T <= 100

1 <= n <= 10000

我的答案:

今天下午的状态还好哈。

这道题有一个疑问,就是怎么表达-1这种状况呢

如何判断能不能变成1呢。Kevin说可以设置全局变量,统计一下递归次数,太多了就返回。

比如程序要求1s中完成,根据计算如果递归超过10000次就超时了,那么就设置5000次。

可是我这题没写超时的处理,也成功了,那就是进入递归以后一层一层耗着吧。。。估计测试用例里没有失败的情况。

另外,查了一下,这个问题会有溢出的风险。不过题目给出的范围并不大,所以可能是白银级别的原因吧,没在这个地方设置障碍。

我看到有人用的while循环,不过这是一道递归类的题目嘛,就写递归吧:

#include <iostream>
using namespace std;
int n[100];

int collatz(int n)
{
	int t=0;
	if(n!=1){
    if(n%2==1) t=1+collatz(3*n+1);
    else t=1+collatz(n/2);  	     	
	}
    return t; 
}

int main()
{
	int t=0,i=0,temp=0;
    cin>>t;
    for(i=0;i<t;i++)
    {
    	cin>>n[i];
	}
	for(i=0;i<t;i++)
    {
    	temp=collatz(n[i]);
    	cout<<temp<<endl;
	}
	return 0;
	
}

递归第四题:

题目:

求一棵二叉树的前序遍历,中序遍历和后序遍历

输入描述:

第一行一个整数n,表示这棵树的节点个数。

接下来n行每行2个整数L和R。第i行的两个整数Li和Ri代表编号为i的节点的左儿子编号和右儿子编号。

输出描述:

输出一共三行,分别为前序遍历,中序遍历和后序遍历。编号之间用空格隔开。

样例输入:

5

2 3

4 5

0 0

0 0

0 0

样例输出:

1 2 4 5 3

4 2 5 1 3

4 5 2 3 1

数据范围及提示:

n <= 16

我的答案:

这题很常规,疯狂递归,也正好复习了第一题不熟悉的结构体和树的基本操作。

但是同学,看题啊,要看题目啊同学!!不看输入描述和输出描述的嘛???数字之间有空格哒傻蛋!!!

代码如下:

#include <iostream>
using namespace std;

struct node
{
	int lc;
	int rc;
};

void preorder(node *arr,int n)
{
	if(n!=0)
	{
		cout<<n<<" ";
		preorder(arr,arr[n].lc);
		preorder(arr,arr[n].rc);
	}
}

void inorder(node *arr,int n)
{
	if(n!=0)
	{
		inorder(arr,arr[n].lc);
		cout<<n<<" ";
		inorder(arr,arr[n].rc);
	}
}

void posorder(node *arr,int n)
{
	if(n!=0)
	{
		posorder(arr,arr[n].lc);
		posorder(arr,arr[n].rc);
		cout<<n<<" ";
	}
}


int main()
{
	int n=0,i=0;
	cin>>n;
	node *arr=new node[n+1];
	for(i=1;i<=n;i++)
	{
		cin>>arr[i].lc>>arr[i].rc;
	}
	preorder(arr,1);
	cout<<endl;
	inorder(arr,1);
    cout<<endl;
    posorder(arr,1);
	
	return 0;	
}

递归第五题:

题目:

汉诺塔问题(又称为河内塔问题),是一个大家熟知的问题。在A,B,C三根柱子上,有n个不同大小的圆盘(假设半径分别为1-n吧),一开始他们都叠在我A上(如图所示),你的目标是在最少的合法移动步数内将所有盘子从A塔移动到C塔。

游戏中的每一步规则如下:

1. 每一步只允许移动一个盘子(从一根柱子最上方到另一个柱子的最上方)

2. 移动的过程中,你必须保证大的盘子不能在小的盘子上方(小的可以放在大的上面,最大盘子下面不能有任何其他大小的盘子)

 

如对于n=3的情况,一个合法的移动序列式:

1 from A to C

2 from A to B

1 from C to B

3 from A to C

1 from B to A

2 from B to C

1 from A to C

 

给出一个数n,求出最少步数的移动序列

输入描述:

一个整数n

输出描述:

第一行一个整数k,代表是最少的移动步数。

接下来k行,每行一句话,N from X to Y,表示把N号盘从X柱移动到Y柱。X,Y属于{A,B,C}

样例输入:

3

样例输出:

7

1 from A to C

2 from A to B

1 from C to B

3 from A to C

1 from B to A

2 from B to C

1 from A to C

数据范围及提示:

n<=10

我的答案:

经典的汉诺塔问题。递归中的经典问题。甚至可以不管逻辑,只面向实际情形。

今天才知道汉诺塔的背景故事:

法国数学家爱德华·卢卡斯曾编写过一个印度的古老传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片:一次只移动一片,不管在哪根针上,小片必须在大片上面。僧侣们预言,当所有的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消灭,而梵塔、庙宇和众生也都将同归于尽。

为了这个先输出次数再输出过程,我简单粗暴地用了两次递归。

测试通过了,也不想再想了。

好像有点感冒。

代码:

#include <iostream>
using namespace std;

int hanoitime(int n,char a2,char b2,char c2)
{
	int t=0,t1=0,t2=0;
	if(n==1) t=1;
	else 
	{
		t1=hanoitime(n-1,a2,c2,b2);
		t2=hanoitime(n-1,b2,a2,c2);
		t=t1+t2+1;
	}
	return t;
}


void hanoiout(int n,char a2,char b2,char c2)
{
	if(n==1)  cout<<n<<" from "<<a2<<" to "<<c2<<endl;
	else 
	{
		hanoiout(n-1,a2,c2,b2);
		cout<<n<<" from "<<a2<<" to "<<c2<<endl;
		hanoiout(n-1,b2,a2,c2);
	}
}
int main()
{
	int n=0,t=0;
	cin>>n;
	t=hanoitime(n,'A','B','C');	
	cout<<t<<endl;
	hanoiout(n,'A','B','C');
	
	return 0;
}

喜升白银!解锁黄金!撒花✿✿ヽ(°▽°)ノ✿

喜悦转瞬即逝,随之而来的是更多更难的题目。

突然觉得人生也是这样吧。成功的快意很快就消散了,随之而来的是更多更艰巨的挑战。

快乐是短暂的,艰难和困苦却是长久的么?

不要这样想呀。

问题的原因不在生活,而在自己。

若是把迎接挑战的过程当成幸福,而成功这一事件不值一提,那么喜悦也是长久的了~

接受黄金段位的挑战吧!我一定要拿下黄金!

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值