回溯法--求解1~n的全排列

17 篇文章 3 订阅

输出1~n的全排列,其中n由键盘输入。

1>解向量的分析
要求所有排列结果,那么任何一个排列方案,就是问题的一个解,如何表示这个解呢?很显然,我们可以用一个数组int X[MAX]; 这就是解向量。
2>解空间树与回溯函数
每一个解元素的范围都在[1,n]范围,其解空间树如图左侧:在这里插入图片描述
回溯函数如图右侧,
该函数围绕着X[1]~ X[n]的求解,从解空间树的根开始深度优先遍历,其中f(k+1)是对一棵子树进行遍历,每到达一个叶子节点【即if(k-1==n)成立时】,则输出从根到该叶子路径上存储的X[1]~X[n]【每一个非叶子节点处所选择的分支,相当于迷宫的一个岔路口的选择,用对应X[]记录了该岔路口的选择】。从代码中可以看出,并没有创建这棵树,该树是有神而无形的“隐形”树。
运行结果图2。
在这里插入图片描述
在这里插入图片描述
3>剪枝函数
图1程序中,对X[k]取值i时没有任何限制,所以输出结果中数字允许重复出现,而全排列问题,应该让X[k]与X[1]~ X[k-1]不重复,那么这就是剪枝逻辑,在没有状态信息时,剪枝函数xianjie(k,i)中通过循环扫描x[1]~x[k-1]看是否已经出现过i,从而使得其对应解空间树的遍历逻辑如图3。
4>状态量的设计
前面剪枝函数中,在判断X[k]能否取i时,用了一个循环,依次检查X[1]~X[k-1]中是否已经出现i。显然可以加入一个状态数组int used[ ],其初值均为0,用used[i]记住在x[1]~x[k-1]中i已经被使用次数,这样程序可以改进为:

int  X[100],used[100],n, cnt=0; //键盘输入的n小于100
int  xianjie(int k, int i)//判断X[k]能否取i
{
   if(used[i] >0)  return 0; //used[i]>0时表示数字i已经被使用过
   //if(cnt>=20)  return 0; //此剪枝可选用:只输出前20个排列结果
   return 1;
}
void  f(int k)
{  int  i;
   if(k-1==n)  { 输出++cnt:    输出X[1]~X[n]; }
   else for(i=1;i<=n;i++)
       if(xianjie(k,i))
       {   
		X[k]=i;
		used[i]++; //数字i已经被使用的次数+1,从而在递归f(k+1)里面used[i]都是增加1之后的值
        f(k+1); //遍历  x[k]取i  处的一颗子树
        used[i]--;//f(k+1)结束后,准备遍历旁边一棵子树(即x[k]取i+1 处的子树),x[1]~x[k-1]没有变化,
	//仅X[k]换成 i+1了,所以在旁边这棵子树中数字i的使用次数应-1
}
}
void main(void)
{
   cout<<"Enter n:";
   cin>>n;
   f(1);
}

归纳:
(1)这是一个“麻雀虽小却五脏俱全”的例子。学会解向量的分析、围绕解向量求解的回溯函数框架、进一步控制解元素取值范围的剪枝函数(即问题提出的各种约束条件)。
(2)弄懂基本原理后,以后基本上可以一步到位的写含有状态量的回溯算法代码,而且只要发现剪枝函数中有循环,基本上都可以考虑设计状态量,从而减轻剪枝函数的运算量。另外状态量应该在 f(k+1)前、后做对称修改。
(3)回溯函数、剪枝函数、输出函数、外加其它辅助函数,区分开来写,而不要将全部的逻辑都裹在一起,这样有利于保持自己思路的清晰,出错时好对症下药。
(4)剪枝函数的注意事项,只有最后一个语句是 return 1; 前面全部是return 0的逻辑语句。其形式基本上如下
if(不满足约束的条件1) return 0;
if(不满足约束的条件2) return 0;

if(不满足约束的条件n) return 0;
return 1; //不需要 “else”

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值