range_minimum_query.h 6.31 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
// Copyright 2010-2018 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// We use the notation min(arr, i, j) for the minimum arr[x] such that i <= x
// and x < j.
// Range Minimum Query (RMQ) is a data structure preprocessing an array arr so
// that querying min(arr, i, j) takes O(1) time. The preprocessing takes
// O(n*log(n)) time and memory.

// Note: There exists an O(n) preprocessing algorithm, but it is considerably
// more involved and the hidden constants behind it are much higher.
//
// The algorithms are well explained in Wikipedia:
// https://en.wikipedia.org/wiki/Range_minimum_query.
//
//
// Implementation: The idea is to cache every min(arr, i, j) where j - i is a
// power of two, i.e. j = i + 2^k for some k. Provided this information, we can
// answer all queries in O(1): given a pair (i, j) find the maximum k such that
// i + 2^k < j and note that
// std::min(min(arr, i, i+2^k), min(arr, j-2^k, j)) = min(arr, i, j).

#ifndef OR_TOOLS_UTIL_RANGE_MINIMUM_QUERY_H_
#define OR_TOOLS_UTIL_RANGE_MINIMUM_QUERY_H_

#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <vector>

#include "ortools/util/bitset.h"

namespace operations_research {
template <typename T, typename Compare = std::less<T>>
class RangeMinimumQuery {
 public:
  explicit RangeMinimumQuery(std::vector<T> array);
  RangeMinimumQuery(std::vector<T> array, Compare cmp);

  // Returns the minimum (w.r.t. Compare) arr[x], where x is contained in
  // [from, to).
  T GetMinimumFromRange(int from, int to) const;

  const std::vector<T>& array() const;

 private:
  // cache_[k][i] = min(arr, i, i+2^k).
  std::vector<std::vector<T>> cache_;
  Compare cmp_;

  DISALLOW_COPY_AND_ASSIGN(RangeMinimumQuery);
};

// RangeMinimumIndexQuery is similar to RangeMinimumQuery, but
// GetMinimumIndexFromRange returns the index for which the minimum is attained.
template <typename T, typename Compare = std::less<T>>
class RangeMinimumIndexQuery {
 public:
  explicit RangeMinimumIndexQuery(std::vector<T> array);
  RangeMinimumIndexQuery(std::vector<T> array, Compare cmp);

  // Returns an index idx from [from, to) such that arr[idx] is the minimum
  // value of arr over the interval [from, to).
  int GetMinimumIndexFromRange(int from, int to) const;

  // Returns the original array.
  const std::vector<T>& array() const;

 private:
  // Returns a vector with values 0, 1, ... n - 1 for a given n.
  static std::vector<int> CreateIndexVector(int n);
  struct IndexComparator {
    bool operator()(int lhs_idx, int rhs_idx) const;
    const std::vector<T> array;
    Compare cmp;
  } cmp_;
  const RangeMinimumQuery<int, IndexComparator> rmq_;
  DISALLOW_COPY_AND_ASSIGN(RangeMinimumIndexQuery);
};

// RangeMinimumQuery implementation
template <typename T, typename Compare>
inline RangeMinimumQuery<T, Compare>::RangeMinimumQuery(std::vector<T> array)
    : RangeMinimumQuery(std::move(array), Compare()) {}

// Reminder: The task is to fill cache_ so that
// cache_[k][i] = min(arr, i, i+2^k) for every k <= Log2(n) and i <= n-2^k.
// Note that cache_[k+1][i] = min(cache_[k][i], cache_[k][i+2^k]), hence every
// row can be efficiently computed from the previous.
template <typename T, typename Compare>
RangeMinimumQuery<T, Compare>::RangeMinimumQuery(std::vector<T> array,
                                                 Compare cmp)
    : cache_(MostSignificantBitPosition32(array.size()) + 1),
      cmp_(std::move(cmp)) {
  const int array_size = array.size();
  cache_[0] = std::move(array);
  for (int row_idx = 1; row_idx < cache_.size(); ++row_idx) {
    const int row_length = array_size - (1 << row_idx) + 1;
    const int window = 1 << (row_idx - 1);
    cache_[row_idx].resize(row_length);
    for (int col_idx = 0; col_idx < row_length; ++col_idx) {
      cache_[row_idx][col_idx] =
          std::min(cache_[row_idx - 1][col_idx],
                   cache_[row_idx - 1][col_idx + window], cmp_);
    }
  }
}

template <typename T, typename Compare>
inline T RangeMinimumQuery<T, Compare>::GetMinimumFromRange(int from,
                                                            int to) const {
  DCHECK_LE(0, from);
  DCHECK_LT(from, to);
  DCHECK_LE(to, array().size());
  const int log_diff = MostSignificantBitPosition32(to - from);
  const int window = 1 << log_diff;
  const std::vector<T>& row = cache_[log_diff];
  return std::min(row[from], row[to - window], cmp_);
}

template <typename T, typename Compare>
inline const std::vector<T>& RangeMinimumQuery<T, Compare>::array() const {
  return cache_[0];
}

// RangeMinimumIndexQuery implementation
template <typename T, typename Compare>
inline RangeMinimumIndexQuery<T, Compare>::RangeMinimumIndexQuery(
    std::vector<T> array)
    : RangeMinimumIndexQuery(std::move(array), Compare()) {}

template <typename T, typename Compare>
RangeMinimumIndexQuery<T, Compare>::RangeMinimumIndexQuery(std::vector<T> array,
                                                           Compare cmp)
    : cmp_({std::move(array), std::move(cmp)}),
      rmq_(CreateIndexVector(cmp_.array.size()), cmp_) {}

template <typename T, typename Compare>
inline int RangeMinimumIndexQuery<T, Compare>::GetMinimumIndexFromRange(
    int from, int to) const {
  return rmq_.GetMinimumFromRange(from, to);
}

template <typename T, typename Compare>
inline bool RangeMinimumIndexQuery<T, Compare>::IndexComparator::operator()(
    int lhs_idx, int rhs_idx) const {
  return cmp(array[lhs_idx], array[rhs_idx]);
}

template <typename T, typename Compare>
std::vector<int> RangeMinimumIndexQuery<T, Compare>::CreateIndexVector(int n) {
  std::vector<int> result(n, 0);
  std::iota(result.begin(), result.end(), 0);
  return result;
}

template <typename T, typename Compare>
inline const std::vector<T>& RangeMinimumIndexQuery<T, Compare>::array() const {
  return cmp_.array;
}
}  // namespace operations_research
#endif  // OR_TOOLS_UTIL_RANGE_MINIMUM_QUERY_H_