回溯法

回溯法

    回 溯法也称为试探法,该方法首先暂时放弃关于问题规模大小的限制,并将问题的候选解按某种顺序逐一枚举和检验。当发现当前候选解不可能是解时,就选择下一个 候选解;倘若当前候选解除了还不满足问题规模要求外,满足所有其他要求时,继续扩大当前候选解的规模,并继续试探。如果当前候选解满足包括问题规模在内的 所有要求时,该候选解就是问题的一个解。在回溯法中,放弃当前候选解,寻找下一个候选解的过程称为回溯。扩大当前候选解的规模,以继续试探的过程称为向前 试探。

    回溯法的一般流程和技术

    在用回溯法求解有关问题的过程中,一般是一边建树,一边遍历该树。在回溯法中我们一般采用非递归方法。下面,我们给出回溯法的非递归算法的一般流程:

    在用回溯法求解问题,也即在遍历状态空间树的过程中,如果采用非递归方法,则我们一般要用到栈的数据结构。这时,不仅可以用栈来表示正在遍历的树的结点,而且可以很方便地表示建立孩子结点和回溯过程。

    【问题】 组合问题

    问题描述:找出从自然数1,2,…,n中任取r个数的所有组合。

    采用回溯法找问题的解,将找到的组合以从小到大顺序存于a[0],a[1],…,a[r-1]中,组合的元素满足以下性质:

    (1) a[i+1]>a[i],后一个数字比前一个大;

    (2) a[i]-i<=n-r+1。

    按回溯法的思想,找解过程可以叙述如下:

    首 先放弃组合数个数为r的条件,候选组合从只有一个数字1开始。因该候选解满足除问题规模之外的全部条件,扩大其规模,并使其满足上述条件(1),候选组合 改为1,2。继续这一过程,得到候选组合1,2,3。该候选解满足包括问题规模在内的全部条件,因而是一个解。在该解的基础上,选下一个候选解,因 a[2]上的3调整为4,以及以后调整为5都满足问题的全部要求,得到解1,2,4和1,2,5。由于对5不能再作调整,就要从a[2]回溯到a[1], 这时,a[1]=2,可以调整为3,并向前试探,得到解1,3,4。重复上述向前试探和向后回溯,直至要从a[0]再回溯时,说明已经找完问题的全部解。 按上述思想写成程序如下:

    【程序】

# define MAXN 100

int a[MAXN];

void comb(int m,int r)

{ int i,j;

  i=0;

  a[i]=1;

  do {

       if (a[i]-i<=m-r+1

       { if (i==r-1)

         { for (j=0;j<r;j++)  printf(“%4d”,a[j]);

           printf(“/n”);

          }

         a[i]++;

         continue;

        }

       else

       { if (i==0) return;

         a[--i]++;

        }

       } while (1)

  }

main()

{ comb(5,3);

  }

    【问题】 填字游戏

    问题描述:在3×3个方格的方阵中要填入数字1到N(N≥10)内的某9个数字,每个方格填一个整数,似的所有相邻两个方格内的两个整数之和为质数。试求出所有满足这个要求的各种数字填法。

    可 用试探发找到问题的解,即从第一个方格开始,为当前方格寻找一个合理的整数填入,并在当前位置正确填入后,为下一方格寻找可填入的合理整数。如不能为当前 方格找到一个合理的可填证书,就要回退到前一方格,调整前一方格的填入数。当第九个方格也填入合理的整数后,就找到了一个解,将该解输出,并调整第九个的 填入的整数,寻找下一个解。

    为 找到一个满足要求的9个数的填法,从还未填一个数开始,按某种顺序(如从小到大的顺序)每次在当前位置填入一个整数,然后检查当前填入的整数是否能满足要 求。在满足要求的情况下,继续用同样的方法为下一方格填入整数。如果最近填入的整数不能满足要求,就改变填入的整数。如对当前方格试尽所有可能的整数,都 不能满足要求,就得回退到前一方格,并调整前一方格填入的整数。如此重复执行扩展、检查或调整、检查,直到找到一个满足问题要求的解,将解输出。

    回溯法找一个解的算法:

{ int m=0,ok=1;

  int n=8;

  do { if (ok) 扩展;

       else 调整;

       ok=检查前m个整数填放的合理性;

      } while ((!ok||m!=n)&&(m!=0))

  if (m!=0) 输出解;

  else 输出无解报告;

  }

    如果程序要找全部解,则在将找到的解输出后,应继续调整最后位置上填放的整数,试图去找下一个解。

相应的算法如下:

    回溯法找全部解的算法:

{ int m=0,ok=1;

  int n=8;

  do { if (ok)

       { if (m==n)

         { 输出解;

           调整;

          }

         else 扩展;

        }

       else 调整;

       ok=检查前m个整数填放的合理性;

      } while (m!=0);

  }

    为 了确保程序能够终止,调整时必须保证曾被放弃过的填数序列不会再次实验,即要求按某种有许模型生成填数序列。给解的候选者设定一个被检验的顺序,按这个顺 序逐一形成候选者并检验。从小到大或从大到小,都是可以采用的方法。如扩展时,先在新位置填入整数1,调整时,找当前候选解中下一个还未被使用过的整数。 将上述扩展、调整、检验都编写成程序,细节见以下找全部解的程序。

    【程序】

# include <stdio.h>

# define N 12

void write(int a[ ])

{ int i,j;

  for (i=0;i<3;i++)

  { for (j=0;j<3;j++) printf(“%3d”,a[3*i+j]);

    printf(“/n”);

   }

  scanf(“%*c”);

  }

 

int b[N+1];

int a[10];

int isprime(int m)

{ int i;

  int primes[ ]={2,3,5,7,11,17,19,23,29,-1};

  if (m==1||m%2=0) return 0;

  for (i=0;primes[i]>0;i++)

  if (m==primes[i]) return 1;

  for (i=3;i*i<=m;)

  { if (m%i==0) return 0;

    i+=2;

   }

  return 1;

  }

 

int checkmatrix[][3]={ {-1},{0,-1},{1,-1},{0,-1},{1,3,-1},

{2,4,-1},{3,-1},{4,6,-1},{5,7,-1}};

int selectnum(int start)

{ int j;

  for (j=start;j<=N;j++)

  if (b[j]) return j;

  return 0;

  }

 

int check(int pos)

{ int i,j;

  if (pos<0) return 0;

  for (i=0;(j=checkmatrix[pos][i])>=0;i++)

      if (!isprime(a[pos]+a[j]) return 0;

  return 1;

  }

 

int extend(int pos)

{ a[++pos]=selectnum(1);

  b[a][pos]]=0;

  return pos;

  }

 

int change(int pos)

{ int j;

  while (pos>=0&&(j=selectnum(a[pos]+1))==0)

  b[a[pos--]]=1;

  if (pos<0) return –1;

  b[a[pos]]=1;

  a[pos]=j;

  b[j]=0;

  return pos;

  }

 

void find()

{ int ok=0,pos=0;

  a[pos]=1;

  b[a[pos]]=0;

  do { if (ok)

          if (pos==8)

          { write(a);

            pos=change(pos);

           }

          else

            pos=extend(pos);

       else

          pos=change(pos);

       ok=check(pos);

      } while (pos>=0)

  }

 

void main()

{ int i;

  for (i=1;i<=N;i++) b[i]=1;

  find();

  }

好的,让我为您解答这个问题回溯法是一种递归算法,用于在解空间中搜索问题的解。在找n个数中r个数组合时,可以使用回溯法来搜索所有可能的组合。 时间树是一种可视化工具,用于表示算法的执行过程。在回溯法中,时间树可以用来表示搜索过程中的决策路径和回溯过程。 下面是使用回溯法找n个数中r个数组合的时间树: 1. 从第一个数开始选择,将其加入组合中,继续向下搜索 2. 从第二个数开始选择,将其加入组合中,继续向下搜索 3. 从第三个数开始选择,将其加入组合中,继续向下搜索 4. 从第四个数开始选择,将其加入组合中,得到一个符合条件的组合,记录下来 5. 回溯到第三个数,将其从组合中删除,尝试选下一个数 6. 从第五个数开始选择,将其加入组合中,得到一个符合条件的组合,记录下来 7. 回溯到第二个数,将其从组合中删除,尝试选下一个数 8. 从第四个数开始选择,将其加入组合中,得到一个符合条件的组合,记录下来 9. 回溯到第一个数,将其从组合中删除,尝试选下一个数 10. 从第二个数开始选择,将其加入组合中,继续向下搜索 11. 从第三个数开始选择,将其加入组合中,继续向下搜索 12. 从第四个数开始选择,将其加入组合中,得到一个符合条件的组合,记录下来 13. 回溯到第三个数,将其从组合中删除,尝试选下一个数 14. 从第五个数开始选择,但由于已经选了四个数,不符合条件,回溯到第二个数 15. 回溯到第一个数,将其从组合中删除,尝试选下一个数 16. 从第三个数开始选择,但由于已经选了两个数,不符合条件,回溯到第一个数 17. 从第二个数开始选择,但由于已经选了一个数,不符合条件,回溯到根节点 18. 搜索结束,得到所有符合条件的组合。 以上是一个简单的示例,实际的搜索过程可能会更加复杂。但是,使用时间树可以帮助我们更好地理解回溯法的执行过程,并且方便我们调试和优化算法
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值