数据结构与算法分析 ——回溯算法之收费公路重建问题

数据结构与算法分析第十章回溯算法之收费公路重建问题

 

一、  问题描述:

设给定N个点p1, p2,…,pn,它们位于x-轴上。xi是pi点的x坐标。这N个点确定了N(N-1)/2个点间距离。显然,如果给定点集,那么容易以O(N2)事件构造距离集合。收费公路重建问题是从这些距离重新构造出点集。

 

二、解题思路:

回溯算法:

对于大多数的回溯算法,最方便的实现方法是使用递归,该算法一直沿某条可能路线执行,直至得到确定结果,如果结果是正确的,则成功返回;否则回溯到该路线的上一步,尝试另外一条可能路线。如此程序要么找到正确路线,返回路线;要么不断回溯,尝试所有路线,发现任何路线均不可行,返回失败。

 

收费公路重建:

1、若给定该问题的一个解,则可以通过对所有的点加上一个偏移量而构建无穷多其他的解,因此将第一个点置于0处;

2、由于距离集合D中最大值为max,因此max处存在一个点,从D中删除max,现在点集X中有两个点位置已确定,起点x,终点max;

3、取D中的最大值m,则新点横坐标只存在两种可能,位于m处,或者(max-m)处;

4、先假定新点位于m处,则该点与X中已确定点的点距d也确定了,如果d中的每个值均在D中,那么说明该位置可能合适,更新D和X,执行步骤3(如果步骤3返回true,执行结束,否则还原D和X,执行步骤5);否则执行步骤5;

5、假定新点位于(max-m)处,重新判断点距,如果点距均在D中,更新D和X,执行步骤3(如果步骤3返回true,执行结束,否则还原D和X,返回false);否则无适合点集满足所需,返回false。

 

简要总结:

  每次取集合D中的最大值,则新点位置只存在两种可能。每假定新点位置,则该点与集合X中其他点的距离便确定了,首先判断这些距离是否位于D中,如果不都在,则新点位置不合适;否则确定新点在此位置,继续向下执行。新点位置在两处均不合适,说明上一个新点位置选取失败,回溯。

 

三、编程实现:

  1、代码(注:传入的参数dList必须已按从小到大排序,得到的点的横坐标未排序):

 


public class RebuildRoad {

       protected static int maxLen;


       public void start(LinkedList<Integer>dList, int[] pArray) {

              maxLen =dList.removeLast();

              pArray[0] = 0;

              pArray[1] = maxLen;

 

              build(dList, pArray, 2);

       }

 

       private boolean build(LinkedList<Integer> dList, int[] pArray, int left) {

              if (dList.isEmpty()) {//说明已经找到点集,返回

                     return true;

              }

 

              LinkedList<Integer>dCopy = new LinkedList<Integer>();

              dCopy.addAll(dList); // 创建dList的副本,便于恢复dList

 

              int max =dList.getLast();

              boolean res = false;

 

              res = takeStep(dList,dCopy, pArray, left, max);

              if (res)

                     return true;

 

              int lmax = maxLen - max;// max和lmax总是成对出现的

              res = takeStep(dList,dCopy, pArray, left, lmax);

 

              return res;

       }

 

       private boolean takeStep(LinkedList<Integer> dList,

                     LinkedList<Integer>dCopy, int[] pArray, int left, int max) {

 

              int[] dists = newint[left]; // 存放新点与其他个点的距离

              boolean find = true;

              boolean flag = true;

 

              for (int i = 0; i <left; i++) {

                     dists[i] =Math.abs(max - pArray[i]);

                     if(!dList.contains(dists[i])) {

                            flag =false;

                            break;

                     }

              }

 

              if (flag) { // 如果新点放置在max处,与其他各点的距离均在dList中

                     for (int i = 0; i< left; i++) { // 从dList中移除各个距离

                            if(!dList.remove(new Integer(dists[i]))) {

                                   find= false;

                                   break;

                            }

                     }

 

                     if (find) {

                            pArray[left++]= max;

                            find =build(dList, pArray, left);

                            if (find)

                                   returntrue;

                            else //撤销对数组的操作

                                   left--;

                     }

              }

 

              if (flag) { // 执行到这里,说明此路不通,且dList已改变,需要恢复

                     dList.removeAll(dList);

                     dList.addAll(dCopy);

              }

 

              return false;

       }

}

 


2、结果: