题解思路
本题要求按顺序处理教室租借订单,当某个订单无法满足时(即从第 天到第
天中至少有一天的剩余教室数量不足
个),立即停止并输出该订单编号。如果所有订单均可满足,则输出 0。
优化策略
-
二分:
- 二分查找第一个无法满足的订单编号。假设前
个订单可以满足,则尝试更大的
;否则,尝试更小的
。
- 最终,
记录最后一个满足的订单编号。若
,则所有订单满足;否则,
是第一个不满足的订单。
- 二分查找第一个无法满足的订单编号。假设前
-
差分数组验证:
- 对于前
个订单,使用差分数组高效计算每天的总需求:
- 初始化差分数组
(大小
,初始为 0)。
- 对每个订单
:
(若
)
- 计算前缀和
,得到每天的总需求。若存在
,则前
个订单不满足。
- 初始化差分数组
- 恢复差分数组:验证后恢复
为初始状态,避免影响后续验证。
- 对于前
复杂度分析
- 时间复杂度:
- 二分次数
。
- 每次验证
(
为当前订单数),最坏
。
- 二分次数
- 空间复杂度:
,用于存储差分数组和教室数量。
代码实现
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 1000010;
int n, m;
int r[MAXN];
int d[MAXN], s[MAXN], t[MAXN];
long long diff[MAXN * 2]; // 差分数组,开大避免越界
// 检查前x个订单是否满足
bool check(int x) {
// 应用前x个订单的差分
for (int i = 1; i <= x; i++) {
diff[s[i]] += d[i];
if (t[i] + 1 <= n + 1) {
diff[t[i] + 1] -= d[i];
}
}
long long cur = 0;
bool valid = true;
// 计算前缀和并检查每天需求
for (int i = 1; i <= n; i++) {
cur += diff[i];
if (cur > r[i]) {
valid = false;
break;
}
}
// 恢复差分数组
for (int i = 1; i <= x; i++) {
diff[s[i]] -= d[i];
if (t[i] + 1 <= n + 1) {
diff[t[i] + 1] += d[i];
}
}
return valid;
}
int main() {
memset(diff, 0, sizeof(diff));
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d", &r[i]);
}
for (int i = 1; i <= m; i++) {
scanf("%d %d %d", &d[i], &s[i], &t[i]);
}
int left = 1, right = m;
int ans = 0; // 最后一个满足的订单编号
while (left <= right) {
int mid = (left + right) >> 1;
if (check(mid)) {
ans = mid; // 前mid个订单满足
left = mid + 1;
} else {
right = mid - 1;
}
}
if (ans == m) {
printf("0\n");
} else {
printf("-1\n%d\n", ans + 1);
}
return 0;
}
。 二分查找:
- 初始化
,
,
。
- 当
时,取中点
,调用
验证前
个订单。
- 若满足,更新
并搜索更大的区间;否则,搜索更小区间。
当你需要实现二分查找时,按此流程思考:
明确目标:你要找什么?(第一个≥?最后一个≤?精确匹配?)
定义条件:什么情况下 mid 是候选解?
找第一个≥target → nums[mid] >= target
找最后一个≤target → nums[mid] <= target
确定搜索方向: 候选解的更优解在哪个方向?
更小的索引 → 向左搜索 更大的索引 → 向右搜索
编写代码:
条件成立时 → 向"更优解"方向搜索
条件不成立时 → 向"可能包含解"的方向搜索
验证边界:
target小于所有元素
target大于所有元素
target等于第一个/最后一个元素
target在数组中间

京公网安备 11010502036488号