#P3279. 第1题-找到通信质量最高的基站
-
1000ms
Tried: 886
Accepted: 108
Difficulty: 5
所属公司 :
华为
时间 :2025年5月28日-暑期实习
-
算法标签>双指针
第1题-找到通信质量最高的基站
题解
题目描述
在一条长度为 N 的道路上,路口编号从 0 到 N−1。每个路口 i 都安装了一个基站,并统计了当前接入人数为 crossroads[i]。每个基站的信号覆盖范围为前后各 k 个路口,即第 i 号基站覆盖区间为 [i−k,i+k](边界越界取到 0 或 N−1)。行驶在第 i 号路段(即在路口 i 和 i+1 之间)时,手机可连接所有覆盖该路段的基站,用户应选择接入人数最少的基站。
思路
要在每个路段上快速求出滑动区间内的最小值位置,可用双端队列(deque)维护“单调队列”,以支持区间左右端点同时移动的场景:
-
定义区间端点
- 第 i 号路段的左端点:Li=max(0,i+1−k)
- 右端点:Ri=min(N−1,i+k)
-
初始化
- 对应第 0 号路段,区间为 [0,min(N−1,k)],将所有下标依次入队,同时维护队内从队首到队尾对应的 crossroads[⋅] 值呈单调递增。
-
滑动更新
对每个后续路段 i(从 1 到 N−2):- 右端扩展:若 Ri>Ri−1,将新的下标 Ri 按“单调”规则加入队尾。
- 左端收缩:若队首元素的下标 idx<Li,则将其弹出。
- 队首即最小:此时队首存储的下标即为区间内最小值位置,令 ret[i]=队首下标。
整个过程对每个下标最多入队一次、出队一次,时间复杂度为 O(N)。
C++
#include <bits/stdc++.h>
using namespace std;
vector<int> bestStation(const vector<int>& crossroads, int k) {
int N = crossroads.size();
if (N < 2 || k < 1 || k > N) return {}; // 非法输入
vector<int> ret(N-1);
deque<int> dq;
// 初始化第0号路段的区间 [0, min(N-1, k)]
int R0 = min(N-1, k);
for (int i = 0; i <= R0; ++i) {
// 单调队列:新元素比尾部大时直接入队,否则弹尾
while (!dq.empty() && crossroads[i] <= crossroads[dq.back()]) {
dq.pop_back();
}
dq.push_back(i);
}
ret[0] = dq.front();
// 依次处理后续路段
for (int i = 1; i < N-1; ++i) {
int Li = max(0, i+1-k);
int Ri = min(N-1, i+k);
// 扩展右端
if (Ri > R0) {
int idx = Ri;
while (!dq.empty() && crossroads[idx] <= crossroads[dq.back()]) {
dq.pop_back();
}
dq.push_back(idx);
R0 = Ri;
}
// 收缩左端
while (!dq.empty() && dq.front() < Li) {
dq.pop_front();
}
ret[i] = dq.front();
}
return ret;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
vector<int> crossroads;
int x;
// 读取 crossroads
string line;
if (!getline(cin, line)) {
cout << -1;
return 0;
}
istringstream iss(line);
while (iss >> x) crossroads.push_back(x);
int k;
if (!(cin >> k)) {
cout << -1;
return 0;
}
auto ans = bestStation(crossroads, k);
if (ans.empty()) {
cout << -1;
} else {
for (int i = 0; i < (int)ans.size(); ++i) {
if (i) cout << ' ';
cout << ans[i];
}
}
return 0;
}
Python
from collections import deque
import sys
def best_station(crossroads, k):
n = len(crossroads)
if n < 2 or k < 1 or k > n:
return None # 非法输入
ret = [0] * (n-1)
dq = deque()
# 初始化第0号路段
R0 = min(n-1, k)
for i in range(R0+1):
while dq and crossroads[i] <= crossroads[dq[-1]]:
dq.pop()
dq.append(i)
ret[0] = dq[0]
# 滑动其余路段
for i in range(1, n-1):
Li = max(0, i+1-k)
Ri = min(n-1, i+k)
if Ri > R0:
idx = Ri
while dq and crossroads[idx] <= crossroads[dq[-1]]:
dq.pop()
dq.append(idx)
R0 = Ri
while dq and dq[0] < Li:
dq.popleft()
ret[i] = dq[0]
return ret
if __name__ == "__main__":
data = sys.stdin.readline().strip().split()
crossroads = list(map(int, data))
try:
k = int(sys.stdin.readline().strip())
except:
print(-1)
sys.exit(0)
res = best_station(crossroads, k)
if res is None:
print(-1)
else:
print(" ".join(map(str, res)))
Java
import java.io.*;
import java.util.*;
public class Main {
// 使用双端队列维护滑动区间最小值下标
public static List<Integer> bestStation(int[] crossroads, int k) {
int n = crossroads.length;
if (n < 2 || k < 1 || k > n) return null; // 非法输入
List<Integer> ret = new ArrayList<>();
Deque<Integer> dq = new LinkedList<>();
// 初始化第0号路段区间 [0, min(n-1,k)]
int R0 = Math.min(n-1, k);
for (int i = 0; i <= R0; i++) {
while (!dq.isEmpty() && crossroads[i] <= crossroads[dq.peekLast()]) {
dq.pollLast();
}
dq.offerLast(i);
}
ret.add(dq.peekFirst());
// 处理后续路段
for (int i = 1; i < n-1; i++) {
int Li = Math.max(0, i+1-k);
int Ri = Math.min(n-1, i+k);
if (Ri > R0) {
int idx = Ri;
while (!dq.isEmpty() && crossroads[idx] <= crossroads[dq.peekLast()]) {
dq.pollLast();
}
dq.offerLast(idx);
R0 = Ri;
}
while (!dq.isEmpty() && dq.peekFirst() < Li) {
dq.pollFirst();
}
ret.add(dq.peekFirst());
}
return ret;
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] parts = br.readLine().trim().split("\\s+");
int[] crossroads = Arrays.stream(parts).mapToInt(Integer::parseInt).toArray();
int k;
try {
k = Integer.parseInt(br.readLine().trim());
} catch (Exception e) {
System.out.println(-1);
return;
}
List<Integer> ans = bestStation(crossroads, k);
if (ans == null) {
System.out.println(-1);
} else {
for (int i = 0; i < ans.size(); i++) {
if (i > 0) System.out.print(" ");
System.out.print(ans.get(i));
}
}
}
}
题目内容
闹市区中有一条马路,马路从0号路口开始,到N−1号路口结束,在每个路口都架设了最新技术的通信基站,每个基站的信号可以覆盖前后各k个路口的范围,即第i个路口上的基站,可以覆盖[i−k,i+k]这两个路口之间的马路,因此用户的手机处于多个基站的覆盖范围中。每个基站会统计当前接入人数,为保障最佳通信质量,用户手机应选择连接人数最少的基站进行通讯。
这条马路一共N个路口,小明从0号路口出发向前走,求小明在每个路段中的最佳通讯基站。不考虑处于路口中间的特殊场景,只考虑在每个路段中的场景,例如第1个路段应为0号路口到1号路口之间的路段,如果基站覆盖范围k=2,此时最佳基站应为0、1、2中连接人数最少的基站。
输入描述
输入为两行
第一行长度为N的整数数组crossroads[],数组元素以空格分隔,其中crossroads[i]表示i号路口基站的当前接入人数。1<=crossroads,length数组长度<=105,0<=crossroads[i]<=102
第二行为基站覆盖范围k,1<=k<=crossroads.length
非法输入返回−1
输出描述
返回一个数组ret,ret[i]表示i路段上最佳基站的编号,数组元素之间以空格分隔。例如0号路口到1号路口的路段,为0号路段,其最佳基站用ret[0]表示。
样例1
输入
3 5 8 7 6 7 4
2
输出
0 0 1 4 6 6
说明
小明在第1段路时,位于0号和1号基站中间,此时可以连接到0、1、2这3个基站,而这三个基站中连接人数最小的是0号基站(3个人),因此输出数组第一个元素应为0号基站。小明位于第2段路时,位于1号和 2号基站中间,此时可以连接到0、1、2、3这4个基站,选择其中连接人数最小的基站,即0号基站(3人),因此输出数组第二个元素为0号基站。以此类推。
样例2
输入
9 8 7 6 7 8 9
2
输出
2 3 3 3 3 4
说明
小明在第1段路时,可以连接到的基站为0、1、2这3个基站,其中连接人数最小的基站为2号基站(7人连接),因此输出数组第一个元素为2号基站。以此类推。