‘壹’ 算法的常用设计方法有哪些
算法设计是一件非常困难的工作,经常采用的算法设计技术主要有迭代法、穷举搜索法、递推法、贪婪法、回溯法、分治法、动态规划法等等。
另外,为了更简洁的形式设计和藐视算法,在算法设计时又常常采用递归技术,用递归描述算法。
‘贰’ 四皇后问题求解
本章内容来自《妙趣横生的算法》一书中。
回溯法是一种非常有效,适用范围相当广泛的算法设计思想。许多复杂的问题,规模较大的问题都可以使用回溯法求解。因此回溯法又有“通用解题方法”的美称。
回溯法的基本思想是:在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度 探索 解空间树。当 探索 到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续 探索 下去;如果该结点不包含问题的解,那就说明以该结点为根结点的子树一定不包含“剪枝”操作。
如果应用回溯法求解问题的所有解,要回溯到解空间树的树根,这样根结点的所有子树都被 探索 到才结束。如果只要求解问题的一个解,问题的最终解,因此要跳过对以该结点为根的子树的系统 探索 ,逐层向其祖先结点回溯。这个过程叫做解空间树的那么在 探索 解空间树时,只要搜索到问题的一个解就可以结束了。
应用回溯法的思想求解四皇后问题
分析:
上面一节中已经详细介绍了回溯法解决四皇后问题的基本过程。在这里将给出具体的算法描述和程序清单。
其实在解决四皇后问题时,并不一定要真的构建出这样一棵解空间树,它完全可以通过一个递归回溯来模拟。所谓解空间树只是一个逻辑上的抽象。当然也可以用树结构来真实地创建出一棵解空间树,不过那样会比较浪费空间资源。
运行结果:
‘叁’ Pascal算法之回溯及递推详细介绍、
在程序编辑过程中,我们可能会遇到这样一类问题,出题者告诉你数列的前几个数,或通过计算机获取了数列的前几个数,要求编程者求出第N项数或所有的数列元素(如果可以枚举的话),或求前N项元素之和。这种从已知数据入手,寻找规则,推导出后面的数的算法,称这递推算法。
在处理递推问题时,我们有时遇到的递推关系是十分明显的,简单地写出递推关系式,就可以逐项递推,即由第i项推出第i+1项,我们称其为显示递推关系。但有的递推关系,要经过仔细观察,甚至要借助一些技巧,才能看出它们之间的关系,我们称其为隐式的递推关系。
递推算法的关键是认真分析题意,发现递推关系,正确写出递推公式,求得边界条件,然后用循环实现即可。总结
1.递推算法的基本形式,是指编程者让计算机循环求得一序列数值,而后一项的计算结果是由上一项或多项推导出来的。有时,我们为求得一个数列的某一项,我们不得不从第一项开始,逐个计算前面每一项的值。虽然这样做的效率不很高,但它能帮助我们解决许多实际问题。
2.无论是顺着还是逆着递推,其关键是递推公式是否正确,边界条件是否正确。二者有一个出错。则所有递推结果将都是错误的。
【】
例1:已知数列1,2,5,10,17,26,37...求数列的第n项。
通过分析,我们可以发现,数列后面的数在前一项的基础上以1,3,5,7,9,11的规律增长,则可以得出增长规律的表达式为2*n-3,也就是a(1)=1,a(n)=a(n-1)+2*n-3;
还有一个规律,我们可以发现每一项依次为从0开始的自然数的平方再加1,即
(n-1)2+1,第一项(1-1)2+1,第二项(2-1)2+1,第三项(3-1)2+1... 例2:阶梯问题:题目的意思是:有N级阶梯,可以一步走上一级,也可以一步走两级,求从阶梯底走到顶端可以有多少种不同的走法。
这是一个隐式的递推关系,如果编程者不能找出这个递推关系,可能就无法做出这题来。我们来分析一下:走上第一级的方法只有一种,走上第二级的方法却有两种(两次走一级或一次走两级),走上第三级的走法,应该是走上第一级的方法和走上第二级的走法之和(因从第一级和第二级,都可以经一步走至第三级,也就是说增加的第三级有两种处理方法,第一种就是直接作为一级走一步,那么就和两级的走法一致,另一种就是与第二级合并作一步走,那么就和一级的走法一致,加起来就是一级的方法和二级的走法之和),推广到走上第i级,是走上第i-1级的走法与走上第i-2级的走法之和。很明显,这是一个菲波拉契数列。到这里,读者应能很熟练地写出这个程序。在以后的程序习题中,我们可能还会遇到菲波拉契数列变形以后的结果:如f(i)=f(i-1)+2f(i-2),或f(i)=f(i-1)+f(i-2)+f(i-3)等。
例3:猴子吃桃问题:山中住有五只猴。一天,老大看见一堆桃子,想把桃子据为已有,却担心让老二老三知道了说自己太贪心。于是将桃分成相等的两份,却发现剩余一个,于是,老大吃掉这一个以后,再带走这堆桃的二分之一。第二天,老二也看到了这堆桃,其想法和做法与老大一样,老三老四老五也和他们的兄长想到一块去了。结果等老五吃完一个,带走一半以后,这堆桃还剩余11个。请编程计算当初这堆桃共有多少个。
这个下题目明眼人一看便知,我们如果按兄弟吃桃把桃的相反顺序倒推过去,就能知道当初桃子的总数。其递推的公式是a[n-1]=a[n]*2+1。递推的初始值是a[5]=11(又称边界条件),待求a[0]的值。相信现在大家能很容易就能写出正确的程序。在这里不过是想说明一下,递推算法不仅可以顺着推、也可逆着推的道理。
【】
回溯算法也叫试探法,它是一种系统地搜索问题的解的方法。回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。
用回溯算法解决问题的一般步骤为:
一、定义一个解空间,它包含问题的解。
二、利用适于搜索的方法组织解空间。
三、利用深度优先法搜索解空间。
四、利用限界函数避免移动到不可能产生解的子空间。
问题的解空间通常是在搜索问题的解的过程中动态产生的,这是回溯算法的一个重要特性。
回溯法是一个既带有系统性又带有跳跃性的的搜索算法。它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。否则,进入该子树,继续按深度优先的策略进行搜索。回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。而回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。这种以深度优先的方式系统地搜索问题的解的算法称为回溯法,它适用于解一些组合数较大的问题.
[递推是学习材料,回溯是网络]
江静
游客 回答于:2010-8-29 15:05:18
递归
递归是计算机科学的一个重要概念,递归的方法是程序设计中有效的方法,采用递归编写
程序能是程序变得简洁和清晰.
2.1 递归的概念
1.概念
一个过程(或函数)直接或间接调用自己本身,这种过程(或函数)叫递归过程(或函数).
如:
procere a;
begin
.
.
.
a;
.
.
.
end;
这种方式是直接调用.
又如:
procere b; procere c;
begin begin
. .
. .
. .
c; b;
. .
. .
. .
end; end;
这种方式是间接调用.
例1计算n!可用递归公式如下:
1 当 n=0 时
fac(n)={n*fac(n-1) 当n>0时
可编写程序如下:
program fac2;
var
n:integer;
function fac(n:integer):real;
begin
if n=0 then fac:=1 else fac:=n*fac(n-1)
end;
begin
write('n=');readln(n);
writeln('fac(',n,')=',fac(n):6:0);
end.例2 楼梯有n阶台阶,上楼可以一步上1阶,也可以一步上2阶,编一程序计算共有多少种不同的走法.
设n阶台阶的走法数为f(n)
显然有
1 n=1
f(n)={2 n=2
f(n-1)+f(n-2) n>2
可编程序如下:
program louti;
var n:integer;
function f(x:integer):integer;
begin
if x=1 then f:=1 else
if x=2 then f:=2 else f:=f(x-1)+f(x-2);
end;
begin
write('n=');read(n);
writeln('f(',n,')=',f(n))
end.
2.2 如何设计递归算法
1.确定递归公式
2.确定边界(终了)条件
练习:
用递归的方法完成下列问题
1.求数组中的最大数
2.1+2+3+...+n
3.求n个整数的积
4.求n个整数的平均值
5.求n个自然数的最大公约数与最小公倍数
6.有一对雌雄兔,每两个月就繁殖雌雄各一对兔子.问n个月后共有多少对兔子?7.已知:数列1,1,2,4,7,13,24,44,...求数列的第 n项.
2.3典型例题
例3 梵塔问题
如图:已知有三根针分别用1,2,3表示,在一号针中从小放n个盘子,现要求把所有的盘子
从1针全部移到3针,移动规则是:使用2针作为过度针,每次只移动一块盘子,且每根针上
不能出现大盘压小盘.找出移动次数最小的方案.
程序如下:
program fanta;
var
n:integer;
procere move(n,a,b,c:integer);
begin
if n=1 then writeln(a,'--->',c)
else begin
move(n-1,a,c,b);
writeln(a,'--->',c);
move(n-1,b,a,c);
end;
end;
begin
write('Enter n=');
read(n);
move(n,1,2,3);
end.
例4 快速排序
快速排序的思想是:先从数据序列中选一个元素,并将序列中所有比该元素小的元素都放到它的右边或左边,再对左右两边分别用同样的方法处之直到每一个待处理的序列的长度为1, 处理结束.
程序如下:
program kspv;
const n=7;
type
arr=array[1..n] of integer;
var
a:arr;
i:integer;
procere quicksort(var b:arr; s,t:integer);
var i,j,x,t1:integer;
begin
i:=s;j:=t;x:=b[i];
repeat
while (b[j]>=x) and (j>i) do j:=j-1;
if j>i then begin t1:=b[i]; b[i]:=b[j];b[j]:=t1;end;
while (b[i]<=x) and (i<j) do i:=i+1;
if i<j then begin t1:=b[j];b[j]:=b[i];b[i]:=t1; end
until i=j;
b[i]:=x;
i:=i+1;j:=j-1;
if s<j then quicksort(b,s,j);
if i<t then quicksort(b,i,t);
end;
begin
write('input data:');
for i:=1 to n do read(a[i]);
writeln;
quicksort(a,1,n);
write('output data:');
for i:=1 to n do write(a[i]:6);
writeln;
end.
练习:
1.计算ackerman函数值:
n+1 m=0
ack(m,n)={ ack(m-1,1) m<>0 ,n=0
ack(m-1,ack(m,n-1)) m<>0,n<>0
求ack(5,4)回溯
回溯是按照某种条件往前试探搜索,若前进中遭到失败,则回过头来另择通路继续搜索.
3.1 回溯的设计
1.用栈保存好前进中的某些状态.
2.制定好约束条件
例1由键盘上输入任意n个符号;输出它的全排列.
program hh;
const n=4;
var i,k:integer;
x:array[1..n] of integer;
st:string[n];
t:string[n];
procere input;
var i:integer;
begin
write('Enter string=');readln(st);
t:=st;
end;
function place(k:integer):boolean;
var i:integer;
begin
place:=true;
for i:=1 to k-1 do
if x[i]=x[k] then
begin place:=false; break end ;
end;
procere print;
var i:integer;
begin
for i:=1 to n do write(t[x[i]]);
writeln;
end;
begin
input;
k:=1;x[k]:=0;
while k>0 do
begin
x[k]:=x[k]+1;
while (x[k]<=n) and (not place(k)) do x[k]:=x[k]+1;
if x[k]>n then k:=k-1
else if k=n then print
else begin k:=k+1;x[k]:=0 end
end ;
end.
例2.n个皇后问题:
program hh;
const n=8;
var i,j,k:integer;
x:array[1..n] of integer;
function place(k:integer):boolean;
var i:integer;
begin
place:=true;
for i:=1 to k-1 do
if (x[i]=x[k]) or (abs(x[i]-x[k])=abs(i-k)) then
place:=false ;
end;
procere print;
var i:integer;
begin
for i:=1 to n do write(x[i]:4);
writeln;
end;
begin
k:=1;x[k]:=0;
while k>0 do
begin
x[k]:=x[k]+1;
while (x[k]<=n) and (not place(k)) do x[k]:=x[k]+1;
if x[k]>n then k:=k-1
else if k=n then print
else begin k:=k+1;x[k]:=0 end
end ;
end.
回溯算法的公式如下:
3.2 回溯算法的递归实现
由于回溯算法用一栈数组实现的,用到栈一般可用递归实现。
上述例1的递归方法实现如下:
program hh;
const n=4;
var i,k:integer;
x:array[1..n] of integer;
st:string[n];
t:string[n];
procere input;
var i:integer;
begin
write('Enter string=');readln(st);
t:=st;
end;
function place(k:integer):boolean;
var i:integer;
begin
place:=true;
for i:=1 to k-1 do
if x[i]=x[k] then
begin place:=false; break end ;
end;
procere print;
var i:integer;
begin
for i:=1 to n do write(t[x[i]]);
writeln;readln;
end;
procere try(k:integer);
var i :integer;
begin
if k=n+1 then begin print;exit end;
for i:=1 to n do
begin
x[k]:=i;
if place(k) then try(k+1)
end
end;
begin
input;
try(1);
end.
例2:n皇后问题的递归算法如下:
程序1:
program hh;
const n=8;
var i,j,k:integer;
x:array[1..n] of integer;
function place(k:integer):boolean;
var i:integer;
begin
place:=true;
for i:=1 to k-1 do
if (x[i]=x[k]) or (abs(x[i]-x[k])=abs(i-k)) then
place:=false ;
end;
procere print;
var i:integer;
begin
for i:=1 to n do write(x[i]:4);
writeln;
end;
procere try(k:integer);
var i:integer;
begin
if k=n+1 then begin print; exit end;
for i:= 1 to n do
begin
x[k]:=i;
if place(k) then try(k+1);
end;
end ;
begin
try(1);
end.
程序2:
说明:当n=8 时有30条对角线分别用了l和r数组控制,
用c数组控制列.当(i,j)点放好皇后后相应的对角线和列都为false.递归程序如下:
program nhh;
const n=8;
var s,i:integer;
a:array[1..n] of byte;
c:array[1..n] of boolean;
l:array[1-n..n-1] of boolean;
r:array[2..2*n] of boolean;
procere output;
var i:integer;
begin
for i:=1 to n do write(a[i]:4);
inc(s);writeln(' total=',s);
end;
procere try(i:integer);
var j:integer;
begin
for j:=1 to n do
begin
if c[j] and l[i-j] and r[i+j] then
begin
a[i]:=j;c[j]:=false;l[i-j]:=false; r[i+j]:=false;
if i<n then try(i+1) else output;
c[j]:=true;l[i-j]:=true;r[i+j]:=true;
end;
end;
end;
begin
for i:=1 to n do c[i]:=true;
for i:=1-n to n-1 do l[i]:=true;
for i:=2 to 2*n do r[i]:=true;
s:=0;try(1);
writeln;
end.练习:
1.找出所有从m个元素中选取n(n<=m)元素的组合。
2.设有A,B,C,D,E 5人从事j1,j2,j3,j4,j5 5项工作每人只能从事一项,它们的
效益表如下:求最佳安排,使效益最高.
3.N个数中找出M个数(从键盘上输入正整数N,M后再输入N个正数),要求从N个数中
找出若干个数,使它们的和为M,把满足条件的数组找出来,并统计组数.
4.地图着色。如下图12个区域用4种颜色着色要求相邻的区域着不同的颜色
5.将任意一正整数(1<n<100)分解成若干正整数的和.
如:4=1+1+1+1
=2+1+1
=2+2
=3+1.
‘肆’ 算法设计的过程一般是什么样子
您好,楼主
算法设计就是把问题的解决步骤通过计算机编程语言来实现。
大概步骤如下:
1.分析问题:输入什么/输出什么/条件什么/能用什么方法
2.用流程图画出解决方案:决定程序的结构(有三大结构:顺序结构、判断结构、循环结构)
3.算法设计:常见的算法设计方法有:穷举法/迭代法/递推法/递归法/回溯法/贪婪法/分治法。
4.程序设计:这个就需要变成语言来实现的。
‘伍’ 简述回溯法的2种算法框架,并分别举出适合用这两种框架解决的一个问题实例
回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
基本思想
在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。(其实回溯法就是对隐式图的深度优先搜索算法)。 若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。 而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束
一般表达
可用回溯法求解的问题P,通常要能表达为:对于已知的由n元组(x1,x2,…,xn)组成的一个状态空间E={(x1,x2,…,xn)∣xi∈Si ,i=1,2,…,n},给定关于n元组中的一个分量的一个约束集D,要求E中满足D的全部约束条件的所有n元组。其中Si是分量xi的定义域,且 |Si| 有限,i=1,2,…,n。我们称E中满足D的全部约束条件的任一n元组为问题P的一个解。
解问题P的最朴素的方法就是枚举法,即对E中的所有n元组逐一地检测其是否满足D的全部约束,若满足,则为问题P的一个解。但显然,其计算量是相当大的。
规律
我们发现,对于许多问题,所给定的约束集D具有完备性,即i元组(x1,x2,…,xi)满足D中仅涉及到x1,x2,…,xi的所有约束意味着j(j<=i)元组(x1,x2,…,xj)一定也满足d中仅涉及到x1,x2,…,xj的所有约束,i=1,2,…,n。换句话说,只要存在0≤j≤n-1,使得(x1,x2,…,xj)违反d中仅涉及到x1,x2,…,xj的约束之一,则以(x1,x2,…,xj)为前缀的任何n元组(x1,x2,…,xj,xj+1,…,xn)一定也违反d中仅涉及到x1,x2,…,xi的一个约束,n≥i≥j。因此,对于约束集d具有完备性的问题p,一旦检测断定某个j元组(x1,x2,…,xj)违反d中仅涉及x1,x2,…,xj的一个约束,就可以肯定,以(x1,x2,…,xj)为前缀的任何n元组(x1,x2,…,xj,xj+1,…,xn)都不会是问题p的解,因而就不必去搜索它们、检测它们。回溯法正是针对这类问题,利用这类问题的上述性质而提出来的比枚举法效率更高的算法。
‘陆’ 五大基本算法——回溯法
回溯法是一种选优搜索法(试探法)。
基本思想:将问题P的状态空间E表示成一棵高为n的带全有序树T,把求解问题简化为搜索树T。搜索过程采用 深度优先搜索 。搜索到某一结点时判断该结点是否包含原问题的解,如果包含则继续往下搜索,如果不包含则向祖先回溯。
通俗来说,就是利用一个树结构来表示解空间,然后从树的根开始深度优先遍历该树,到不满足要求的叶子结点时向上回溯继续遍历。
几个结点:
扩展结点:一个正在产生子结点的结点称为扩展结点
活结点:一个自身已生成但未全部生成子结点的结点
死结点:一个所有子结点已全部生成的结点
1、分析问题,定义问题解空间。
2、根据解空间,确定解空间结构,得 搜索树 。
3、从根节点开始深度优先搜索解空间(利用 剪枝 避免无效搜索)。
4、递归搜索,直到找到所要求的的解。
1、子集树
当问题是:从n个元素的集合S中找出满足某种性质的子集时,用子集树。
子集树必然是一个二叉树。常见问题:0/1背包问题、装载问题。
遍历子集树时间复杂度:O(2^n)
2、排列树
当问题是:确定n个元素满足某种排列时,用排列数。常见问题:TSP旅行商问题,N皇后问题。
遍历排列树时间复杂度:O(n!)
通俗地讲,结合Java集合的概念,选择哪种树其实就是看最后所得结果是放入一个List(有序)里,还是放入一个Set(无序)里。
剪枝函数能极大提高搜索效率,遍历解空间树时,对于不满足条件的分支进行剪枝,因为这些分支一定不会在最后所求解中。
常见剪枝函数:
约束函数(对解加入约束条件)、限界函数(对解进行上界或下界的限定)
满足约束函数的解才是可行解。
1、0/1背包问题
2、TSP旅行商问题
3、最优装载问题
4、N-皇后问题
具体问题可网络详细内容。
‘柒’ 算法设计里面分治法、贪心法、动态规划法、回溯法、分枝限界法各是什么意思
你这个问题问的内容太多了,还是看网络吧:
分治法:http://ke..com/view/1583824.htm?fr=aladdin
贪心算法:http://ke..com/view/298415.htm
动态规划:http://ke..com/view/28146.htm
回溯算法:http://ke..com/view/6056523.htm
分支限界法:http://ke..com/view/4479359.htm
‘捌’ 算法设计
这道题用递归效率较低.
由于确定了是分成三个数,可以用三个培梁for循环枚举实现.
当然,在枚举的时候可以对范岩乱围进行一定限定,来提高效率.
int main()
{
int i, j, n;
cin >> n;
for ( i = n; i >= n/3; --i ) //最大的数肯定比n/3大;
for ( j = i; j >= 0; --j ) //第二个数小于等于i
if (( i + j <= n )&&( n - i - j <= j )) //第三个数只要n - i - j就可以了,这样不用判断和了
cout << n << " = " << i << " + " << j << " + " << n - i - j << endl;
return 0;
}
一般来说,当拆分的个数一定且不是很大时,用循环枚举是最好的方法.
因为用递归同样有很多冗余,且传递参数需要耗费不少时间.而循环还可以做更多有效的剪枝.
分成四个数的配枣运时候完全可以用循环来做.当要求拆分的个数较多时,则可以把循环化为递归.
void search( int start, int end, int num )
{
if ( num==n ) {
判断和;
返回上一层递归;
} else {
for ( i = start; i >= end; --i )
search( i, 0, num + 1 );
}
}
若要求输出一个数的所有拆分方案,比如6=6,....6=1+1+1+1+1+1,那就可以用回溯法来做.