Bridge

2013-05-09 Robert Zhang 更多博文 » 博客 » GitHub »

原文链接 http://huiming.io/2013/05/09/bridge.html
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


分析:如果每次都让最快的人陪别人过桥,然后他再返回陪另一个人过桥,这是最佳方案吗?以sample输入为例:有4个人,过桥时间分别是:1 2 5 10。按前述策略的过桥方案为:

1 10

1

1 5

1

1 2

共19秒。但按照sample的过桥策略只要17秒:<!--more-->

1 2

1

5 10

2

1 2

该策略其实是:先让最快的2个人过桥,让最快的一个返回送手电,再让最慢的两个人过桥,让第二快的人返回送手电,最后2个人一起过桥。这是最佳方案吗?

如果把要过桥的人按照过桥时间从小到大排列得到数列:a1, a2, ..., a(n-1), a(n)。那么第二种策略可以表述为:

a1 a2

a1

a(n-1) a(n)

a2

经这4步,在最快的2个人的协助下,最慢的2个人过了桥,共用时:a1+2a2+a(n)秒;然后,最快的两个人接着再协助还没有过河的人中最慢的两个人过河;最后,最快的2个人一起过河,或者(在最后还剩3个人的情况下):

a1 a3

a1

a1 a2

在第一种策略下,送最慢的2个人过河需要的时间:

a1 a(n-1)

a1

a1 a(n)

a1

经这4步,在最快的一个人的协助下,最慢的2个人过了桥,共用时:2a1+a(n-1)+a(n)秒。同样是送2个最慢的人过河,两种策略的时间差为:

delta = a1+2a2+a(n) - (2a1+a(n-1)+a(n)) = 2a2 - (a1 + a(n-1)) = (a2 - a1) - (a(n-1) - a2)

第二种策略只在delta小于0的情况下才优于第一种策略。另外我们注意到:如果有a(n-1)使得上面的delta不小于0,则对于a(n-2)直到a3,如果用这些值分别代替上式中的a(n-1),delta都不小于0;也就是说,这这些情况下,第一种策略都不逊于第二种策略。

仔细思考,根据数列的情况,交替运用这2种策略,即可快速过河。

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

inline int cross(vector<vector<int> > & order, int fast, int slow = 0) {
  if (!slow) {
    order.push_back(vector<int>(1, fast));
    return fast;
  }
  vector<int> group(2);
  group[0] = fast;
  group[1] = slow;
  order.push_back(group);
  return slow;
}

int min_time(vector<int> & people, vector<vector<int> > & order) {
  if (people.size() == 1) {
    order.push_back(vector<int>(1, people[0]));
    return people[0];
  }
  sort(people.begin(), people.end());
  int t = 0;
  int p0 = people[0];
  int p1 = people[1];
  while (!people.empty()) {
    int size = people.size();
    if (size == 2) {
      t += cross(order, p0, p1);
      people.clear();
    }
    else if (size == 3) {
      t += cross(order, p0, people[2]);
      t += cross(order, p0);
      t += cross(order, p0, p1);
      people.clear();
    }
    else {
      if (p1 - p0 < people[size - 2] - p1) {
        t += cross(order, p0, p1);
        t += cross(order, p0);
        t += cross(order, people[size - 2], people.back());
        t += cross(order, p1);
        people.pop_back();
        people.pop_back();
      }
      else {
        t += cross(order, p0, people.back());
        t += cross(order, p0);
        people.pop_back();
      }
    }
  }
  return t;
}

int main() {
  int n_case = 0;
  cin >> n_case;
  for (int i = 0; i < n_case; i++) {
    int n = 0;
    cin >> n;
    vector<int> people(n);
    int j;
    for (j = 0; j < n; j++)
      cin >> people[j];
    vector<vector<int> > order;
    int t = min_time(people, order);
    if (i != 0)
      cout << endl;
    cout << t << endl;
    for (j = 0; j < order.size(); j++) {
      for (int k = 0; k < order[j].size(); k++) {
        if (k != 0)
          cout << ' ';
        cout << order[j][k];
      }
      cout << endl;
    }
  }
}