回溯法

  1. 如果上期的“百钱买百鸡”中鸡的种类数是变化的,用枚举法就无能为力了,这里介绍另一种算法——回溯法。  
  2.   
  3. 回溯法是一种既带有系统性又带有跳跃性的搜索法,它的基本思想是:在搜索过程中,当探索到某一步时,发现原先的选择达不到目标,就退回到上一步重新选择。它主要用来解决一些要经过许多步骤才能完成的,而每个步骤都有若干种可能的分支,为了完成这一过程,需要遵守某些规则,但这些规则又无法用数学公式来描述的一类问题。下面通过实例来了解回溯法的思想及其在计算机上实现的基本方法。  
  4.   
  5. 1、从N个自然数(1,2,…,n)中选出r个数的所有组合。  
  6.   
  7. 算法分析:设这r个数为a1,a2,…ar,把它们从大到小排列,则满足:  
  8.   
  9. 1)    a1>a2>…>ar;  
  10.   
  11. 2)    其中第i位数(1<=i<=r)满足ai>r-i;  
  12.   
  13. 我们按以上原则先确定第一个数,再逐位生成所有的r个数,如果当前数符合要求,则添加下一个数;否则返回到上一个数,改变上一个数的值再判断是否符合要求,如果符合要求,则继续添加下一个数,否则返回到上一个数,改变上一个数的值……按此规则不断循环搜索,直到找出r个数的组合,这种求解方法就是回溯法。  
  14.   
  15. 如果按以上方法生成了第i位数ai,下一步的的处理为:  
  16.   
  17. 1)    若ai>r-i且i=r,则输出这r个数并改变ai的值:ai=ai-1;  
  18.   
  19. 2)    若ai>r-i且i≠r,则继续生成下一位ai+1=ai-1;  
  20.   
  21. 3)    若ai<=r-i,则回溯到上一位,改变上一位数的值:ai-1=ai-1-1;  
  22.   
  23. 算法实现步骤:  
  24.   
  25. 第一步:输入n,r的值,并初始化;  i:=1;a[1]:=n;  
  26.   
  27. 第二步:若a[1]>r-1则重复:  
  28.   
  29. 若a[i]>r-i,①若i=r,则输出解,并且a[i]:=a[i]-1;  
  30.   
  31. ②若i<>r,则继续生成下一位:a[i+1]:=a[i]-1; i:=i+1;  
  32.   
  33. 若 a[i]<=r-i,则回溯:i:=i-1; a[i]:=a[i]-1;  
  34.   
  35. 第三步:结束;  
  36.   
  37. 程序实现  
  38.   
  39. var  n,r,i,j:integer;  
  40.   
  41.  a:array[1..10] of integer;  
  42.   
  43. begin  
  44.   
  45.   readln(n,r);i:=1;a[1]:=n;  
  46.   
  47.   repeat  
  48.   
  49.     if  a[i]>r-i  then {符合条件 }  
  50.   
  51.       if i=r  then  {输出}  
  52.   
  53. begin  
  54.   
  55. for j:=1 to r do write(a[j]:3);  
  56.   
  57. writeln;  
  58.   
  59.         a[i]:=a[i]-1;  
  60.   
  61.       end  
  62.   
  63. else {继续搜索}  
  64.   
  65. begin a[i+1]:=a[i]-1; i:=i+1;end  
  66.   
  67.     else{回溯}  
  68.   
  69.       begin  i:=i-1; a[i]:=a[i]-1;end;  
  70.   
  71.   until  a[1]=r-1;  
  72.   
  73. end.  
  74.   
  75. 下面我们再通过另一个例子看看回溯在信息学奥赛中的应用。  
  76.   
  77. 2 数的划分(noip2001tg)  
  78.   
  79. 问题描述 整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。  
  80.   
  81. 例如:n=7,k=3,下面三种分法被认为是相同的。  
  82.   
  83. 115;    151;    511;  
  84.   
  85. 问有多少种不同的分法。  
  86.   
  87. 输入:n,k      (6<n<=2002<=k<=6)  
  88.   
  89. 输出:一个整数,即不同的分法。  
  90.   
  91. 样例  
  92.   
  93. 输入:  7   3  
  94.   
  95. 输出:4   {四种分法为:1,1,51,2,41,3,32,2,3;}  
  96.   
  97. 算法分析:此题可以用回溯法求解,设自然数n拆分为a1,a2,…,ak,必须满足以下两个条件:  
  98.   
  99. 1)    n=a1+a2+…+ak  ;  
  100.   
  101. 2)    a1<=a2<=…<=ak  (避免重复计算);  
  102.   
  103. 现假设己求得的拆分数为a1,a2,…ai,且都满足以上两个条件,设sum=n-a1-a2-…-ai,我们根据不同的情形进行处理:  
  104.   
  105. 1)    如果i=k,则得到一个解,则计数器t加1,并回溯到上一步,改变ai-1的值;  
  106.   
  107. 2)    如果i<k且sum>=ai,则添加下一个元素ai+1;  
  108.   
  109. 3)    如果i<k且sum<ai,则说明达不到目标,回溯到上一步,改变ai-1的值;  
  110.   
  111. 算法实现步骤如下:  
  112.   
  113. 第一步:输入自然数n,k并初始化;t:=0; i:=1;a[i]:=1; sum:=n-1; nk:=n div k;  
  114.   
  115. 第二步:如果a[1]<=nk 重复:  
  116.   
  117. 若i=k,搜索到一个解,计数器t=t+1;并回溯;  
  118.   
  119. 否则:①若sum>=a[i]则继续搜索;  
  120.   
  121. ②若sum<a[i]则回溯;  
  122.   
  123. 搜索时,inc(i);a[i]:=a[i-1];sum:=sum-a[i];  
  124.   
  125. 回溯时,dec(i); inc(a[i]); sum:=sum+a[i+1]-1;  
  126.   
  127. 第三步:输出。  
  128.   
  129. 程序如下:  
  130.   
  131. var  
  132.   
  133.   n,nk,sum,i,k:integer;  
  134.   
  135.   t:longint;  
  136.   
  137.   a:array[1..6]of integer;  
  138.   
  139. begin  
  140.   
  141.   readln(n,k);  
  142.   
  143.   nk:=n div k;  
  144.   
  145.   t:=0; i:=1;a[i]:=1; sum:=n-1;{初始化}  
  146.   
  147.   repeat  
  148.   
  149. if i=k then{判断是否搜索到底}  
  150.   
  151. begin  inc(t); dec(i);inc(a[i]); sum:=sum+a[i+1]-1; end {回溯}  
  152.   
  153.     else begin  
  154.   
  155. if sum>=a[i] then {判断是否回溯}  
  156.   
  157. begin inc(i);a[i]:=a[i-1];sum:=sum-a[i];end{继续搜}  
  158.   
  159.       else begin dec(i); inc(a[i]); sum:=sum+a[i+1]-1; end;{回溯}  
  160.   
  161.     end;  
  162.   
  163.   until a[1]>nk;  
  164.   
  165.   writeln(t);  
  166.   
  167. end.  
  168.   
  169.    
  170.   
  171. 回溯法是通过尝试和纠正错误来寻找答案,是一种通用解题法,在NOIP中有许多涉及搜索问题的题目都可以用回溯法来求解 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值