• 《数据结构与算法分析: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 }
  • 相关阅读:
    DDD实战2 创建领域基础类库项目
    DDD实战1
    MVC中 跳转出某一个Area的方法
    在codefirst迁移的时候 为新增的属性添加一个默认值
    在.net MVC项目中使用ajax进行数据验证
    将枚举转成SelectListItem
    在.net MVC项目的区域中使用模板页
    在MVC项目中分页使用MvcPager插件
    通过控制台查看电脑中.net framework的版本
    全面了解浏览器(内核)发展史
  • 原文地址:https://www.cnblogs.com/zhuli19901106/p/3830299.html
Copyright © 2020-2023  润新知