D - 点検スケジュール / Inspection Schedule 解説 by admin
GLM 5.2 (High)Overview
In this problem, we need to place maintenance intervals of length \(K\) on a highway such that the distance constraint is satisfied: every point on the highway must be within \(M\) km of some maintenance interval. Under this constraint, we want to minimize the number of event venues that overlap with the maintenance intervals.
Analysis
Looking at the constraints, \(T \leq 1000\) and \(N \leq 12\) are very small. In particular, since \(N\) is extremely small, we can use a bitmask brute-force search to try all possibilities of “which event venues to avoid overlapping with.”
First, let us consider the case where \(T \leq M\). In this case, we can satisfy the condition without placing any maintenance intervals (\(p=0\)), meaning we do not overlap with any event venues. The answer is \(0\).
Next, let’s consider how to determine if we can place the maintenance intervals to satisfy the distance constraint when the set of event venues to avoid is fixed. If we let the start point of a maintenance interval be \(s\), the distance constraint can be rephrased as: “the start point of the next interval must always be within \(M\) of the end of the previous interval.” Thus, a greedy approach of repeatedly choosing the largest \(s\) that is less than or equal to the current reachable limit \(R\) is optimal.
To avoid event venue \(i\), the maintenance interval \((s, s+K)\) must not overlap with \((L_i, R_i)\). The condition for \(s\) to satisfy this is \(s \leq L_i - K\) or \(s \geq R_i\). Therefore, if \(s\) falls within the interval \((L_i - K, R_i)\), we mark it as “forbidden (cannot be placed).”
After marking the forbidden intervals for all event venues we decided to avoid, we use the greedy method to determine if we can reach the endpoint \(T\). If we can reach it, we record the maximum number of event venues we were able to avoid.
Algorithm
- Special Case Handling: If \(T \leq M\), we can choose to place zero maintenance intervals, which results in \(0\) affected event venues. Output \(0\) and terminate.
- Bitmask Brute-force: For each bitmask from \(0\) to \(2^N - 1\), the set bits represent the event venues we target to “avoid.”
- Setting Forbidden Intervals: For each avoided event venue \(i\), any \(s\) in the range of possible start positions (\(0 \leq s \leq T - K\)) that falls within the interval \((L_i - K, R_i)\) is marked as forbidden (cannot be placed).
- Greedy Check:
- As a precomputation, for each position \(i\), find the “largest non-forbidden \(s\) that is less than or equal to \(i\)”.
- Initialize the current reachable limit \(current\_R\) to \(M\).
- Repeat the following until \(current\_R\) is at least \(T\):
- Choose the largest non-forbidden \(s\) less than or equal to \(current\_R\).
- If no such \(s\) exists, or if choosing \(s\) does not extend the reachable limit (i.e., we get stuck), then placement is impossible.
- Update \(current\_R\) to \(s + K + M\).
- Calculating the Answer: If placement is possible, update the maximum number of avoided event venues (the number of set bits). After checking all bitmasks, the minimum number of overlapping event venues will be \(N - (\text{maximum avoided})\).
Complexity
- Time Complexity: \(O(2^N \cdot (N + T))\)
- There are \(2^N\) states in the bitmask brute-force. For each state, setting the forbidden intervals and running the greedy check takes \(O(N + T)\) time. Since \(N \leq 12\) and \(T \leq 1000\), this is fast enough.
- Space Complexity: \(O(T)\)
- This is the space required to store the forbidden intervals and the precomputed array.
Implementation Details
In C++, you can easily count the number of set bits using functions like
__builtin_popcount.When searching for the next \(s\) in the greedy algorithm, precomputing “the largest non-forbidden \(s\) less than or equal to \(idx\)” in an array allows the check loop to proceed in \(O(1)\) per step, keeping the overall complexity low.
The “overlapping” condition between a maintenance interval and an event venue is that the intersection of their open intervals is non-empty. Be careful with the boundary conditions (if \(s\) is exactly equal to \(L_i - K\) or \(R_i\), they do not overlap).
Source Code
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int T, N, K, M;
cin >> T >> N >> K >> M;
vector<int> L(N), R_ev(N);
for(int i = 0; i < N; ++i) {
cin >> L[i] >> R_ev[i];
}
// p = 0 の場合、T <= M であれば条件を満たすためイベント会場への影響は0
if (T <= M) {
cout << 0 << endl;
return 0;
}
int max_avoid = 0;
for (int mask = 0; mask < (1 << N); ++mask) {
// 点検区間の開始地点として選べない s を計算
vector<bool> forbidden(T - K + 1, false);
for (int i = 0; i < N; ++i) {
if ((mask >> i) & 1) {
// イベント会場 i と重複しないための条件: s <= L_i - K または s >= R_i
// よって s が (L_i - K, R_i) に含まれる場合は禁止
int s_start = max(0, L[i] - K + 1);
int s_end = min(T - K, R_ev[i] - 1);
for (int s = s_start; s <= s_end; ++s) {
forbidden[s] = true;
}
}
}
// 各 R に対して、R 以下で最大の禁止されていない s を前計算
vector<int> max_valid(T - K + 1);
int current_max = -1;
for (int i = 0; i <= T - K; ++i) {
if (!forbidden[i]) {
current_max = i;
}
max_valid[i] = current_max;
}
// 貪欲法で到達可能か判定
int current_R = M;
bool possible = true;
while (current_R < T) {
int idx = min(current_R, T - K);
if (idx < 0) {
possible = false;
break;
}
int s = max_valid[idx];
if (s == -1) {
possible = false;
break;
}
int next_R = s + K + M;
if (next_R <= current_R) {
possible = false;
break;
}
current_R = next_R;
}
if (possible) {
max_avoid = max(max_avoid, __builtin_popcount(mask));
}
}
// 全体から回避できたイベント会場の数を引いたものが答え
cout << N - max_avoid << endl;
return 0;
}
This editorial was generated by or-glm5.2-high.
投稿日時:
最終更新: