回溯算法本质是穷举,实现手段是递归,在递归的每个阶段,做如下操作:
1.预占资源
2.递归处理子问题
3.若失败,回溯,释放资源
收费公路重建问题:给定N个点,都位于x轴上,
是
点的坐标,那么对于这N个点,有
个两点之间的距离,可以花费
时间排序,得到一个距离的集合。收费公路重建问题是,从这些距离重新构建一个点集。代码如下:
/* 收费公路重建问题 */
int Turnpike(int X[], DistSet D, int N)
{
/* 第一个节点放在坐标0,最后一个节点显然能获取到,取剩余节点中距离最大的,放在右边,
* 这些要求不失一般性*/
X[1] = 0;
X[N] = DeleteMax(D);
X[N - 1] = DeleteMax(D);
if (X[N] - X[N - 1] is in D)
{
Remove(X[N] - X[N - 1], D);
return Place(X, D, N, 2, N - 2);
}
return false;
}
/* 收费公路重建问题核心例程 */
int Place(int X[], DistSet D, int N, int Left, int Right)
{
int DMax, Found = false;
/* 递归终止条件,集合中没有距离了,也就是所有节点都被处理好了 */
if (D is empty)
return True;
/* 找集合中的最大距离 */
DMax = FindMax(D);
/* 将点放在右边,对left和right中间的所有点,都在集合D中有距离 */
if (X[j] - DMax is in D for all 1 <= j <left, Right < j <= N)
{
/* 放置点,移除所有距离,递归剩下的部分 */
X[Right] = DMax;
for (1 <= j < left, Right < j <= N)
Delete(X[j] - DMax, D);
Found = Place(X, D, N, Left, Right - 1);
/* 若失败了,那么回溯,回滚资源 */
if (!Found)
for (1 <= j < left, Right < j <= N)
Insert(X[j] - DMax, D);
}
/* 放在右边失败了,尝试放在左边 */
if (!Found && X[N] - DMax - X[j] is in D for all 1 <= j < left,
Right < j<= N)
{
X[Left] = X[N] - DMax;
for (1 <= j < left, Right < j <= N)
Delete(X[N] - DMax - X[j], D);
Found = Place(X, D, N, Left + 1, Right);
if (!Found)
for (1 <= j < left, Right < j <= N)
Insert(X[N] - DMax - X[j], D);
}
return Found;
}
时间分析:若不回溯,那么时间是,排序需要时间
,总时间就是
,一般来说若点分布均匀,那么在整个过程中会发生一次回溯