最近在算法竞赛训练中,我遇到了几道典型的二分查找和贪心算法题目。这些题目看似简单,但想要高效解决却需要深入理解算法原理和灵活运用技巧。下面我将结合具体题目,分享我的解题思路和实现细节。
二分查找是算法竞赛中最常用的技巧之一,其核心思想是通过不断缩小搜索范围来快速定位目标值。在解决最优化问题时,二分查找往往能发挥奇效。
这道题要求找到一个长度不小于L的子串,使其平均数最大。直接枚举所有可能的子串显然不可行,时间复杂度会达到O(n²)。
解决方案:
cpp复制bool check(double mid) {
vector<double> sum(n+1);
for(int i=1; i<=n; i++)
sum[i] = sum[i-1] + a[i] - mid;
double min_val = 0;
for(int i=L; i<=n; i++) {
if(sum[i] - min_val >= 0) return true;
min_val = min(min_val, sum[i-L+1]);
}
return false;
}
时间复杂度分析:
这道题要求将n个点分成k个部落,使得不同部落间的最小距离最大化。
解题思路:
cpp复制bool check(double d) {
init_union_find();
int cnt = n;
for(int i=1; i<=n; i++) {
for(int j=i+1; j<=n; j++) {
if(dist(i,j) < d && find(i) != find(j)) {
unite(i,j);
cnt--;
}
}
}
return cnt >= k;
}
优化技巧:
贪心算法通过局部最优选择来达到全局最优,在特定问题中非常高效。
题目要求在不超支的情况下满足尽可能多的顾客需求。
贪心策略:
cpp复制priority_queue<node> q;
for(int i=1; i<=n; i++) {
sum += a[i];
if(sum < b[i]) {
if(!q.empty() && q.top().mon > b[i]) {
sum += q.top().mon;
q.pop();
ans--;
}
}
if(sum >= b[i]) {
sum -= b[i];
q.push({i, b[i]});
ans++;
}
}
时间复杂度: O(nlogn)
需要在有限时间内修复尽可能多的建筑,每个建筑有修复时间和截止时间。
贪心策略:
cpp复制sort(a+1, a+n+1, [](Build x, Build y){return x.t2 < y.t2;});
priority_queue<int> q;
for(int i=1; i<=n; i++) {
sum += a[i].t1;
q.push(a[i].t1);
if(sum > a[i].t2) {
sum -= q.top();
q.pop();
}
}
在网格图中,普通移动消耗1步,魔法移动消耗1次魔法使用次数。
优化思路:
cpp复制deque<Node> q;
q.push_front({x1,y1,0,0});
while(!q.empty()) {
Node u = q.front(); q.pop_front();
// 普通移动
for(int i=0; i<4; i++) {
int nx = u.x + dx1[i], ny = u.y + dy1[i];
if(valid(nx,ny))
q.push_back({nx,ny,u.t1+1,u.t2});
}
// 魔法移动
for(int i=0; i<4; i++) {
int nx = u.x + dx2[i], ny = u.y + dy2[i];
if(valid(nx,ny))
q.push_front({nx,ny,u.t1,u.t2+1});
}
}
时间复杂度: 从O(nmlog(nm))优化到O(nm)
题目要求对一个序列进行多次区间排序(升序或降序),最后查询某个位置的值。
巧妙解法:
cpp复制bool chk(int mid) {
build_segment_tree(mid);
for(int i=1; i<=m; i++) {
int cnt = query(L[i], R[i]);
if(op[i] == 0) { // 升序
update(R[i]-cnt+1, R[i], 1);
update(L[i], R[i]-cnt, 0);
} else { // 降序
update(L[i], L[i]+cnt-1, 1);
update(L[i]+cnt, R[i], 0);
}
}
return query_pos(q) == 1;
}
时间复杂度: O(nlog²n)
在实际比赛中,我经常遇到的问题是想到算法但实现不够优化。例如在解决P8817假期计划问题时,最初我尝试完全暴力枚举,结果超时。后来通过预处理每个点可达的前三大权值点,将复杂度从O(n⁴)降到了O(n²)。这提醒我,在算法竞赛中,不仅要想出正确解法,还要不断思考如何优化实现。