二分+差分数组经典应用:NOIP2012借教室问题详解
一、问题背景
借教室是NOIP2012提高组的Day2T2,考察了二分查找与差分数组的结合应用。题目需要处理n天中m个订单的教室借用请求,找出第一个无法满足的订单编号。
二、算法核心
二分查找:快速定位第一个导致失败的订单
差分数组:高效处理区间增减操作
可行性检查:验证前mid个订单是否都能被满足
三、完整代码解析
#include <iostream> #include <cstring> using namespace std; const int MAXN = 1e6 + 5; int n, m; int r[MAXN]; // 每天可用教室数 int d[MAXN]; // 每个订单需求量 int s[MAXN], t[MAXN]; // 订单起止日期 int diff[MAXN]; // 差分数组 int temp[MAXN]; // 临时数组 bool check(int mid) { memset(diff, 0, sizeof(diff)); // 构建前mid个订单的差分数组 for (int i = 1; i <= mid; ++i) { diff[s[i]] += d[i]; diff[t[i] + 1] -= d[i]; } // 计算每日实际使用量 for (int i = 1; i <= n; ++i) { temp[i] = temp[i - 1] + diff[i]; if (temp[i] > r[i]) return false; // 发现不满足 } return true; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin >> n >> m; for (int i = 1; i <= n; ++i) cin >> r[i]; for (int i = 1; i <= m; ++i) cin >> d[i] >> s[i] >> t[i]; // 二分查找第一个不满足的订单 int left = 1, right = m, ans = 0; while (left <= right) { int mid = (left + right) >> 1; if (check(mid)) { left = mid + 1; } else { ans = mid; right = mid - 1; } } if (ans) cout << "-1\n" << ans << endl; else cout << "0" << endl; return 0; }
四、关键算法解析
差分数组原理:
diff[i] = a[i] - a[i-1]
区间[l,r]加d转化为:diff[l]+=d, diff[r+1]-=d
通过前缀和还原原数组
二分查找设计:
左边界left=1,右边界right=m
当check(mid)为真时说明前mid个订单都合法
找到第一个使check返回false的mid
复杂度分析:
二分O(logm)
每次check O(n+m)
总复杂度O((n+m)logm)
五、常见优化点
使用快速输入输出加速(ios::sync_with_stdio)
差分数组大小设置为n+2避免越界
临时数组复用节省空间
六、易错点提醒
差分数组还原时需要累加前项
二分查找边界条件的处理
订单编号从1开始的实际意义处理
参考:2012提高组借教室
原创内容 转载请注明出处