天天看点

全排列 回溯法

全排列可以说是最基本的部分了,不过实现的过程还是很有必要学习的,可以说难者不会,会者不难。

大体思路如下:

第一步:从n个数中选取第一个排列的第一个元素,如1;

第一步:从n个数中选取第一个排列的第二个元素,如2;

......

第n步:从n个数中选取第一个排列的第n个元素,如n;

当然不能选重复的。到此,第一个排列已经选出来了。那么第二个排列怎么选呢,其实很简单。

上一个排列执行到第n步后,这个函数不再执行,进行回溯,那么就会回到第n-1步,这时前面的n-1

个数都已经选过了,所以第n-1步选择的就会是n了,然后第n步选择的就是n-1;

所以第一个排列是:1 2 3 。。。n-1 n;

第二个是:1 2 3 。。。n n-1;

以此类推就会输出所有的排列,代码如下。

#include<iostream>
using namespace std;
int n,a[100],v[100];
//a数组用于保存每一次的排列,v数组用于判断数字是不是已经被选过 
void dfs(int dp)//dp从1到n,执行n次,每次选择一个数 
{
	if(dp>n)//dp为n+1的时候,就说明已经选了n个,可以输出了; 
	{
		for(int i=1;i<=n;i++)
		{
			cout<<a[i]<<" ";
		}
		cout<<endl;
		return;
	}
	for(int i=1;i<=n;i++) 
	{
		if(!v[i])//判断是不是选择过 
		{
			a[dp]=i;//保存当前选择 
			v[i]=1;//标记这个数字,防止同一个排列选择相同的数字。 
			dfs(dp+1);
			v[i]=0;//回溯回来的时候一定要清楚标记,不然下一个排列就能选择了,这也是最关键的地方,仔细思考一下。 
		}
	}
} 
int main()
{
	while(cin>>n)
	{
		dfs(1);
	}
}
           

运行结果:

全排列 回溯法

,很明显,这是字典序。

还有另外一种实现的方法,有着很好的用处,可以方便的解决一些搜索的题目。

答题思路就是我选择了一个元素,那么就把这个元素交换到当前这个位置,就不用开一个数组标记了,直接给代码吧,兄弟们

可以自己用笔和纸走一遍,就会明白了,我认为这种思想很有用处的。

#include<iostream>
using namespace std;
int n,a[100];
void dfs(int dp)
{
	if(dp>n)
	{
		for(int i=1;i<=n;i++)
		{
			cout<<a[i]<<" ";
		}
		cout<<endl;
		return;
	}
	for(int i=dp;i<=n;i++)
	{
		swap(a[i],a[dp]);//交换 
		dfs(dp+1);
		swap(a[i],a[dp]);//回溯回来的时候一定要换回来 
	}
}
int main()
{
	while(cin>>n)
	{
		for(int i=1;i<=n;i++)
		{
			a[i]=i;//初始化a数组, 
		}
		dfs(1);
	}
}
           

结果:

全排列 回溯法

很显然,不是字典序,可以和上一个截图对比一下,有的题目会要求用字典序输出,所以要小心一点。

举一反三的时候到了,兄弟们可以做一下这一道题,完全用的就是上面的方法,如果你能写出来,那么就是真的理解了。

题目:

007:素数环

总时间限制: 

1000ms

内存限制: 

131072kB

描述

输入正整数n,把整数1,2,3……n组成一个环,使得相邻两个整数之和均为素数。输出时从整数1开始逆时针排列。同一个环应恰好输出依次。

输入

一行,正整数n。

输出

把这个环从整数1开始逆时针排列。同一个环恰好输出一次。

每一个数之间用空格隔开。

样例输入

6      

样例输出

1 4 3 2 5 6
1 6 5 2 3 4      

大家先试一下,想要代码的可以评论,然后我会把代码给大家,就按照上面的思想就可以了。

还有,大家觉得讲的好的话,可以支持一下作者,当然也可以提建议,,谢谢兄弟们了。

继续阅读