【递归】Fibonacci数列 | 全排列 | n皇后问题

Fibonacci数列

斐波那契数列(Fibonacci sequence),指的是这样一个数列:1、1、2、3、5、8、13、21、34、...

它满足 F(1)=1, F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)

//求解Fibonacci数列第n项 (从第0项开始)
 
#include<stdio.h>

int f(int n)
{
	if(n==1||n==0) return 1;
	return f(n-1)+f(n-2);
}

int main()
{
	int n;
	scanf("%d",&n);
	printf("%d",f(n));
} 

 

全排列

从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列。全排列数f(n)=n!(定义0!=1)。

例如对1、2、3来说,其全排列是(1,2,3)、(1,3,2)、(2,1,3)、(2,3,1)、(3,1,2)、(3,2,1)。

//按递增顺序输出1-n的全排列 
#include<stdio.h>

//定义全局变量,n:省事儿少传一个参数,Used和pos数组:每一次递归都要用到上一层的数据,若是在函数里定义,则一直在刷新= =  
int n,Used[100] = {0},pos[100];

void f(int index)
{
	if(index==n+1) //如果1~n的位置都安插妥当,输出 
	{
		for(int i=1;i<n;i++)
			printf("%d ",pos[i]);
		printf("%d\n",pos[n]);
		return;
	}
	for(int i=1;i<=n;i++)
	{
		if(Used[i]==0) //如果i不在pos[0]-pos[index-1]中 
		{
			pos[index] = i; //将i放在第index的位置上 
			Used[i] = 1; //标记 
			f(index+1); //递归去给剩下的位置安插数字 
			Used[i] = 0; //取消标记 
		}
	}
}

int main()
{
	scanf("%d",&n);
	f(1); //1-n从1开始 
	return 0;
}

 

n皇后问题

n皇后是指在一个n*n的国际象棋棋盘上放置n个皇后,使得这n个皇后两两均不在同一行、同一列、同一条对角线上,求合法的方案数。对角线不单是最大的那两条,只要满足两个皇后横坐标之差的绝对值与纵坐标之差的绝对值相等即可。

如果枚举每一种情况,则是从n^2个位置中选出其中n个,有C\binom{n}{n*n}

但如果这样想,把三个满足条件的其中两条当作结论来使用,就变成了——因为每行每列只能放置一个皇后,如果把n皇后所在的行号依次写出,那么就会是一个1-n的排列(和上面的全排列十分相似了!)。于是就只需要进行1-n的全排列操作,有n!。

//暴力法
#include<stdio.h>
#include<stdlib.h>

int n,Used[100] = {},pos[100],count = 0,flag;

//在上面的全排列基础上稍作修改 
void f(int index)
{
	if(index==n+1)
	{
		flag = 1; //当前顺序ok的标志(在每次位置排满后归置为合理) 
		for(int i=1;i<=n;i++)
		{
			for(int j=i+1;j<=n;j++)
				//i和j代表行,pos[i]和pos[j]代表列。
				//横纵坐标之差的绝对值相等==在同一条对角线上 
				if(abs(i-j)==abs(pos[i]-pos[j])) flag = 0;
		}
		if(flag) count++;
		return;
	}	
	for(int i=1;i<=n;i++)
	{	
		if(Used[i]==0)
		{
			pos[index] = i;
			Used[i] = 1;
			f(index+1);
			Used[i] = 0;
		}
	}
}

int main()
{
	scanf("%d",&n);
	f(1);
	printf("%d",count);
} 

可以优化下算法,让其一边判断,不行就直接返回,不用全部放置完毕后再来一一筛选了。

//回溯法
#include<stdio.h>
#include<stdlib.h>

int n,count = 0,Used[100] = {0},pos[100],flag;

void f(int index)
{
	//判断在之前已经做好了,到这一步直接++了事 
	if(index==n+1)
	{
		count++;
		return;
	}
	for(int i=1;i<=n;i++)
	{ 
		if(Used[i]==0)
		{	
			flag = 1; //当前皇后不会与之前的皇后冲突 
			for(int pre=1;pre<index;pre++)
			{
				//这里i还没有赋值给pos[index],所以用i而不是pos[index] 
				if(abs(pre-index)==abs(i-pos[pre]))
				{
					flag = 0;
					break;  
				}
			}		 
			if(flag)
			{
				pos[index] = i;
				Used[i] = 1;
				f(index+1);
				Used[i] = 0;
			}	
		}		
	}
}

int main()
{
	scanf("%d",&n);
	f(1);
	printf("%d",count);
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值