问题描述:
设给定N个点p1,p2,…,pN位于x轴上,xi为pi的坐标,假设x1=0且这些点从左至右分布。则每一对点之间都对应一个距离|xi,xj|, (i≠j),共计有N(N-1)/2对点产生的距离。 收费公路重建问题是从这些距离重新构造出点集。
简单举例:
设D是距离的集合,并设D={1,2,2,2,3,3,3,3,4,5,5,5,6,7,8,10}。D包含15个元素,因此点集大小N=6。初始设x1=0,进而x6=10。
算法思路:
收费公路重建问题属于回溯算法。因此当我们发现某一x的位置设置不合理时,会撤销该位置,并进一步尝试另外的坐标,而不是完全推到重来。
我们实现回溯的方法是递归。传递同样的参数以及界Left和Right;这两界是我们试图放入的xi的序号。如果D是空集,那么解已经找到,否则继续递归。
在本问题中,我们按照距离大小由大到小的选择xi的位置,因为距离最大只有两个选项,离左端最远和离右端最远。我们首先尝试在右侧找出符合要求的点即使Xright=Dmax(即新点距左端X1的距离为Dmax),如果已经确定的点与当前点的距离都包括在集合D內。那么尝试性的放上这一点,删除相应的距离,并尝试将界改为Left和Right-1,继续递归。如果这些距离不包括在集合D內,或者新一步的递归不成功,那么,递归层层返回,并还原之前删除的集合D中的距离。这一轮递归结束后,再从左侧尝试Xleft = Xn-Dmax(即新点距X6的距离为Dmax)。如果仍然不符条件,则没有这样的分布。
import java.util.Arrays;
public class BackTracking {
// 接收点的数组x,距离的集合d,点的数量n
public static boolean turnpike(int[] x,DistSet d,int n ){
x[1] = 0;
x[n] = d.deleteMax();
x[n-1] = d.deleteMax();
if (d.contains(x[n]-x[n-1])){
d.remove(x[n]-x[n-1]);
return place(x,d,n,2,n-2);
} else return false;
}
public static boolean place (int[] x, DistSet d, int n,int left,int right){
int dmax;
boolean found = false;
if (d.d.isEmpty())
return true;
dmax = d.findMax();
//用于判断新的x加在左侧还是右侧
//因为确定的数分布在左右两侧,因此各需一个布尔变量。又要判断新点在最左侧还是最右侧,所以共需4个布尔变量
boolean rightside1 = true;
boolean leftside1 = true;
boolean rightside2 = true;
boolean leftside2