《数据结构与算法分析:C语言描述》复习——第十章“算法设计技巧”——收费站重建问题

2014.07.07 18:19

简介:

  给定一条数轴上的n个互不重合的点,你可以计算出C(n,2)=n(n-1)/2个距离。如果我给你这些距离值,你能反推出这n个点的坐标吗?

描述:

  首先,考虑到你可以平移这n个点,并且可以左右反转它们得到对称的两种情况,我们不妨假设最靠左的点p0的坐标正好是0

  

  接下来我们通过深度优先搜索回溯的手段逐个求出每个点的坐标。

  起初我们有n(n-1)/2个距离,搜索要如何下手呢?从最长的那个下手。

  比如n=3时,有距离之{3,7,10}。这其中,10能够告诉我们最靠右的点p2坐标一定10,因为两端的两点离得最远。

  由此我们一开始就能够确定两个端点的坐标p0(人为规定为0)和pn-1,接下来中间的n-2个点需要通过搜索来确定。

  

  假设你现在已经确定了i个点的坐标,剩余的距离集合是D(想想当你确定了i个点之后,手里剩下还有多少个距离)。初始的距离集合D拥有n(n-1)/2个距离。

  如果目前D中最大的距离是d=max(D),那么第i+1个点的坐标可能是d或者pn-1-d。

  

  这样产生了两种可能性,对于每一种可能性,你都要第i+1个点与前i个点的距离是否能在D余中找到。

  如果D余中找不到对应的距离值,说明这次搜索已经失败,需要回溯到上层。

  而如果这i次检验全部成功的话,需要从D中去掉i个距离值。

  

  这个搜索与回溯的过程说起来容易,但如果你真的用简单的数组作为数据结构,会发现操作很不方便而且效率低下。

  我们有如下三点需求:

    1. 找出D中最长的边,我需要有序的数据

    2. 查找某个距离值d是否在D中,我需要快速查找(至少不是线性查找)

    3. 回溯时我需要把去掉过的距离值重新放回D中,我需要快速插入(向数组中间插入元素,效率无法忍受)

  基于以上三点需求,平衡树成了最佳选择。于是STL的<map>成为解决这个问题的上佳工具。(begin()与rbegin()有何用处?)

  就算你执意用简单的数组解决这个问题,觉得那样可以偷懒,实际写起来还是既繁琐又低效。(因为往数组中间插入或删除东西实在是很不和谐的操作)

  说到时间复杂度,这种搜索+回溯的解法自然是指数级的。

  也许用八皇后问题来作为回溯的例子更容易些,不过既然作者选了这个我们就学这个吧。

实现:

  1 // Turnpike reconstruction problem.

  2 //    Description:

  3 //        Given n * (n - 1) / 2 distances, find out if you can determine the 

  4 //            relative coordinates of n points.

  5 #include <algorithm>

  6 #include <cmath>

  7 #include <iostream>

  8 #include <map>

  9 #include <vector>

 10 using namespace std;

 11 

 12 int myabs(int x)

 13 {

 14     return x > 0 ? x : -x;

 15 }

 16 

 17 bool turnpikeReconstruction(int idx, vector<int> &x, map<int, int> &dist_set, 

 18     const int far)

 19 {

 20     if (idx == 0) {

 21         return true;

 22     }

 23     

 24     int cur_n;

 25     int cur_far;

 26     int i;

 27     map<int, int>::iterator it;

 28     

 29     cur_n = (int)x.size();

 30     cur_far = dist_set.rbegin()->first;

 31     for (i = 0; i < cur_n; ++i) {

 32         it = dist_set.find(myabs(cur_far - x[i]));

 33         if (it == dist_set.end() || it->second == 0) {

 34             break;

 35         }

 36         --it->second;

 37         if (it->second == 0) {

 38             dist_set.erase(it);

 39         }

 40     }

 41     if (i == cur_n) {

 42         x.push_back(cur_far);

 43         if (turnpikeReconstruction(idx - 1, x, dist_set, far)) {

 44             return true;

 45         }

 46         x.pop_back();

 47     }

 48     cur_n = i;

 49     for (i = 0; i < cur_n; ++i) {

 50         ++dist_set[myabs(cur_far - x[i])];

 51     }

 52     

 53     cur_n = (int)x.size();

 54     cur_far = dist_set.rbegin()->first;

 55     for (i = 0; i < cur_n; ++i) {

 56         it = dist_set.find(myabs(far - cur_far - x[i]));

 57         if (it == dist_set.end() || it->second == 0) {

 58             break;

 59         }

 60         --it->second;

 61         if (it->second == 0) {

 62             dist_set.erase(it);

 63         }

 64     }

 65     if (i == cur_n) {

 66         x.push_back(far - cur_far);

 67         if (turnpikeReconstruction(idx - 1, x, dist_set, far)) {

 68             return true;

 69         }

 70         x.pop_back();

 71     }

 72     cur_n = i;

 73     for (i = 0; i < cur_n; ++i) {

 74         ++dist_set[myabs(far - cur_far - x[i])];

 75     }

 76     

 77     return false;

 78 }

 79 

 80 int main()

 81 {

 82     int i;

 83     int n, n2;

 84     vector<int> x;

 85     int dist;

 86     map<int, int> dist_set;

 87     int far;

 88     

 89     while (cin >> n2 && n2 > 0) {

 90         for (i = 0; i < n2; ++i) {

 91             cin >> dist;

 92             ++dist_set[dist];

 93         }

 94         n = (int)sqrt(n2 * 2.0) + 1;

 95         far = dist_set.rbegin()->first;

 96         --dist_set[far];

 97         if (dist_set.rbegin()->second == 0) {

 98             dist_set.erase(far);

 99         }

100         

101         x.push_back(0);

102         x.push_back(far);

103         if (!turnpikeReconstruction(n - 2, x, dist_set, far)) {

104             cout << "No solution." << endl;

105         } else {

106             sort(x.begin(), x.end());

107             for (i = 0; i < n; ++i) {

108                 cout << x[i] << ' ';

109             }

110             cout << endl;

111         }

112         

113         x.clear();

114         dist_set.clear();

115     }

116     

117     return 0;

118 }

 

你可能感兴趣的:(数据结构与算法)