3時より8時の方が後にあり、8時より11時の方が後にありますが、11時より3時の方が後にありそうです。 時計のように巡回している場合、局所的には大小関係のようなものがありそうでも、推移律が成り立つような大小関係はうまく定義できません。
その巡回周期の半分以下の差しか現れない場合のみ、局所的な大小関係が正しく求められます。 時計の例でいえば、差が六時間(十二時間の半分)以内の問題は正しく解けるということです。
特定の期間に含まれるかの判定
ある時刻q
が、begin
から始まりend
を含まずに終わる期間に含まれるかの判定は、以下のように行えます。
問題設定よりbegin
よりend
が後にあることが保証されるので、期間が巡回周期に近いような場合でも正しく問題が解けます。
constexpr bool includes( size_t begin, size_t end, size_t q ) { if (begin > end) { return begin <=q || q < end; } else { return begin <= q && q < end; } }
循環バッファを使ったキューの走査
全然関係ないですが、循環バッファを使ったキューを走査するとき、どこを走査するべきかは、先の問題と同一の解き方ができます。
template<class T, std::size_t N> class Queue { std::array<T, N> buf; std::size_t head; std::size_t tail; public: Queue() : buf{} head(0), tail(0) {} bool empty() const { return tail == head; } bool full() const { return (tail+1) % N == head; } void push(T elem) { assert( !full() ); buf[tail] = std::move(elem); tail = (tail+1) % N; } void pop() { assert( !empty() ); head = (head+1) % N; } const T& top() const { assert( !empty() ); return buf[head]; } void print() const; };
こんな感じのキューを想定します。tail
は次に要素が来た時に挿入するべき位置、head
は最も古く入れた要素の位置です。
なお、この実装だと、空っぽか満杯かを区別できないため、N-1
要素しか入れることができません。これを防ぐためには、空っぽであるか(あるいは、満杯であるか)のフラグを用意します。
このとき、このキューに入っている要素を全て書き出すには、以下のようにすればよいです(入れた順)。
template<class T, std::size_t N> void Queue::print() const { if (head < tail) { for( std::size_t i = head; i < tail; ++i ) { std::cout << buf[i] << std::endl; } } else { for( std::size_t i =head; i < N; ++i ) { std::cout << buf[i] << std::endl; } for( std::size_t i = 0; i < tail; ++i ) { std::cout << buf[i] << std::endl; } } }