二分,前缀和,差分算法
二分,前缀和,差分算法是解决大规模区间问题的关键优化方法,下面是笔者总结的知识点,仅供参考~
例题P1083借教室
输入样例
4 3
2 5 4 3
2 1 3
3 2 4
4 2 4
输出样例
-1
2
算法思路
核心思想:二分查找 + 差分数组
- 二分查找
- 确定第一个无法满足的订单编号。
- 对订单进行二分,检查前
mid
份订单是否可行。
- 差分数组
- 快速计算区间更新后的每天需求。
- 差分数组
diff
记录区间变化,通过前缀和还原每天总需求。
- 可行性检查
- 计算前
mid
份订单的总需求,若某天需求超过可用教室数,则返回false
。
- 计算前
优化代码示例
#include<iostream>
#include<vector>
using namespace std;
int n,m;
vector<int> num(n);//存储每天租借的教室数量
struct Rent{
int rentNum;
int rentBegin;
int rentEnd;
};
vector<Rent> rent(m);
//分配教室
int dealClass(const vector<int>& count,const vector<Rent>& arr){
for(int i=1;i<=m;i++){//遍历处理每一份订单
for(int j=rent[i].rentBegin;j<=rent[i].rentEnd;j++){//遍历更新区间的每一个值
if(num[j] < rent[i].rentNum){//如果当前教室数量小于申请量
cout<<"-1"<<endl;
cout<<i<<endl;
return 0;
}
num[j] -= rent[i].rentNum;
}
cout<<"0"<<endl;
return 0;
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>num[i];
}
for(int i=1;i<=m;i++){
cin>>rent[i].rentNum>>rent[i].rentBegin>>rent[i].rentEnd;
}
dealClass(num,rent);
return 0;
}
暴力双重循环
#include <iostream>
#include <vector>
using namespace std;
int n, m;
vector<int> r; // 存储每天可租借的教室数量
vector<int> diff; // 差分数组
// 分配教室并检查是否满足
bool allocate() {
diff.assign(n + 2, 0); // 初始化差分数组
for (int i = 0; i < m; ++i) {
int d, s, t;
cin >> d >> s >> t;
// 更新差分数组
diff[s] += d;
if (t + 1 <= n) diff[t + 1] -= d;
}
// 计算前缀和并检查是否满足
int current = 0;
for (int i = 1; i <= n; ++i) {
current += diff[i];
if (current > r[i]) return false;
}
return true;
}
int main() {
cin >> n >> m;
r.resize(n + 1);
for (int i = 1; i <= n; ++i) {
cin >> r[i];
}
if (allocate()) {
cout << 0 << endl;
} else {
cout << -1 << endl;
// 这里需要重新找到第一个不满足的订单(暴力方法)
// 由于原暴力方法无法高效找到,建议使用二分查找
// 此处仅为示例,实际应使用二分查找
cout << "1" << endl; // 示例输出,需修正
}
return 0;
}
暴力方法局限性:
- 无法高效找到第一个不满足的订单
- 时间复杂度高(O (nm))
- 大数据量下会超时
建议优先使用二分查找 + 差分数组的优化方法。