最小信号功率
OPPO技术岗 8月8号笔试 第二题
题目内容
一条笔直的通信线路沿线分布着 nnn 个终端,按顺序编号为 111 到 nnn。第 iii 个终端需要至少 rir_iri 的信号强度才能正常运作。
你计划在某个整数坐标 ccc(1≤c≤n1 \le c \le n1≤c≤n)处设置一座基站,并为其选定一个非负整数发射功率 PPP。信号在传输过程中会随距离衰减:位于 iii 的终端实际收到的信号强度为 P−∣i−c∣P - |i - c|P−∣i−c∣。为保证所有终端都满足需求,必须对每个 iii 都有 P−∣i−c∣≥riP - |i - c| \ge r_iP−∣i−c∣≥ri。
你可以自由选择基站的位置 ccc。请计算在最优选址下,所需的最小发射功率 PPP。
终端数量 nnn 不超过 200000,每个 rir_iri 满足 0≤ri≤1090 \le r_i \le 10^90≤ri≤109。
输入描述
第一行包含一个整数 nnn,表示终端数量。 第二行包含 nnn 个整数 r1,r2,...,rnr_1, r_2, \dots, r_nr1,r2,...,rn,表示每个终端的最低信号需求,相邻整数之间以一个空格分隔。
输出描述
输出一行一个非负整数,表示所需的最小发射功率。
样例1
输入
3
1 2 3
输出
3
说明
我们有 3 个终端,需求 r=1,2,3r = 1, 2, 3r=1,2,3。 计算 A=maxi(ri+i)=max(1+1,2+2,3+3)=6A = \max_i(r_i + i) = \max(1+1, 2+2, 3+3) = 6A=maxi(ri+i)=max(1+1,2+2,3+3)=6, B=maxi(ri−i)=max(1−1,2−2,3−3)=0B = \max_i(r_i - i) = \max(1-1, 2-2, 3-3) = 0B=maxi(ri−i)=max(1−1,2−2,3−3)=0。 最小功率 P=⌈A+B2⌉=⌈6+02⌉=3P = \lceil \frac{A+B}{2} \rceil = \lceil \frac{6+0}{2} \rceil = 3P=⌈2A+B⌉=⌈26+0⌉=3。 将基站设置在位置 c=3c = 3c=3,功率 P=3P = 3P=3: 终端 1 实际接收 3−∣1−3∣=1≥13 - |1-3| = 1 \ge 13−∣1−3∣=1≥1; 终端 2 接收 3−∣2−3∣=2≥23 - |2-3| = 2 \ge 23−∣2−3∣=2≥2; 终端 3 接收 3−∣3−3∣=3≥33 - |3-3| = 3 \ge 33−∣3−3∣=3≥3。 所有终端满足需求,因此最小功率为 3。
样例2
输入
1
0
输出
0
说明
边界情况:只有 1 个终端,需求为 0。 计算 A=0+1=1A = 0+1=1A=0+1=1,B=0−1=−1B = 0-1=-1B=0−1=−1,P=⌈1+(−1)2⌉=0P = \lceil \frac{1+(-1)}{2} \rceil = 0P=⌈21+(−1)⌉=0。 最优选址 c=1c=1c=1,功率 0 即可满足需求。
样例3
输入
5
0 5 2 4 1
输出
6
说明
5 个终端,需求 0,5,2,4,10,5,2,4,10,5,2,4,1。 A=max(0+1,5+2,2+3,4+4,1+5)=max(1,7,5,8,6)=8A = \max(0+1, 5+2, 2+3, 4+4, 1+5) = \max(1,7,5,8,6) = 8A=max(0+1,5+2,2+3,4+4,1+5)=max(1,7,5,8,6)=8, B=max(0−1,5−2,2−3,4−4,1−5)=max(−1,3,−1,0,−4)=3B = \max(0-1,5-2,2-3,4-4,1-5) = \max(-1,3,-1,0,-4) = 3B=max(0−1,5−2,2−3,4−4,1−5)=max(−1,3,−1,0,−4)=3。 P=⌈8+32⌉=6P = \lceil \frac{8+3}{2} \rceil = 6P=⌈28+3⌉=6。 选择 c=2c=2c=2,验证: 终端 1 接收 6−1=5≥06-1=5 \ge 06−1=5≥0; 终端 2 接收 6−0=6≥56-0=6 \ge 56−0=6≥5; 终端 3 接收 6−1=5≥26-1=5 \ge 26−1=5≥2; 终端 4 接收 6−2=4≥46-2=4 \ge 46−2=4≥4; 终端 5 接收 6−3=3≥16-3=3 \ge 16−3=3≥1。 功率 6 可行,且是最小值。
思路
枚举
- 选取任意位置作为发射点,所需最小功率为
max(左侧终端所需功率最大值, 右侧终端所需功率最大值,本身所需功率)。 - 基于1的分析,可以预处理统计出每个位置(包含本身)左/右侧所需功率所需功率最大值。
- 然后枚举发射点,记录最小所需功率。
- 算法总体时间复杂为
O(n)
C++
cpp
#include<bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<long> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
// 左侧包含本身所需最小功率 右侧包含本身所需最小功率
vector<long> left(n), right(n);
int leftMaxIndex = 0;
for (int i = 0; i < n; i++) {
long lMax = a[leftMaxIndex] + (i - leftMaxIndex);
long cur = a[i];
left[i] = max(lMax, cur);
if (cur > lMax) {
leftMaxIndex = i;
}
}
int rightMaxIndex = n -1;
for (int i = n - 1; i >= 0; i--) {
long rMax = a[rightMaxIndex] + (rightMaxIndex - i);
long cur = a[i];
right[i] = max(rMax, cur);
if (cur > rMax) {
rightMaxIndex = i;
}
}
long ans = LONG_MAX;
for (int i = 0; i < n; i++) {
ans = min(ans, max(left[i], right[i]));
}
cout << ans;
return 0;
}
java
java
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
long[] a = new long[n];
for (int i = 0; i < n; i++) {
a[i] = sc.nextLong();
}
// 左侧包含本身所需最小功率 右侧包含本身所需最小功率
long[] left = new long[n];
long[] right = new long[n];
int leftMaxIndex = 0;
for (int i = 0; i < n; i++) {
long lMax = a[leftMaxIndex] + (i - leftMaxIndex);
long cur = a[i];
left[i] = Math.max(lMax, cur);
if (cur > lMax) {
leftMaxIndex = i;
}
}
int rightMaxIndex = n - 1;
for (int i = n - 1; i >= 0; i--) {
long rMax = a[rightMaxIndex] + (rightMaxIndex - i);
long cur = a[i];
right[i] = Math.max(rMax, cur);
if (cur > rMax) {
rightMaxIndex = i;
}
}
long ans = Long.MAX_VALUE;
for (int i = 0; i < n; i++) {
ans = Math.min(ans, Math.max(left[i], right[i]));
}
System.out.println(ans);
}
}
python
python
n = int(input())
a = list(map(int, input().split()))
# 左侧包含本身所需最小功率 右侧包含本身所需最小功率
left = [0] * n
right = [0] * n
leftMaxIndex = 0
for i in range(n):
lMax = a[leftMaxIndex] + (i - leftMaxIndex)
cur = a[i]
left[i] = max(lMax, cur)
if cur > lMax:
leftMaxIndex = i
rightMaxIndex = n - 1
for i in range(n - 1, -1, -1):
rMax = a[rightMaxIndex] + (rightMaxIndex - i)
cur = a[i]
right[i] = max(rMax, cur)
if cur > rMax:
rightMaxIndex = i
ans = float('inf')
for i in range(n):
ans = min(ans, max(left[i], right[i]))
print(ans)
javascript
js
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const input = [];
rl.on('line', line => {
input.push(line.trim());
});
rl.on('close', () => {
const n = Number(input[0]);
const a = input[1].split(/\s+/).map(Number);
// 左侧包含本身所需最小功率 右侧包含本身所需最小功率
const left = new Array(n).fill(0);
const right = new Array(n).fill(0);
let leftMaxIndex = 0;
for (let i = 0; i < n; i++) {
const lMax = a[leftMaxIndex] + (i - leftMaxIndex);
const cur = a[i];
left[i] = Math.max(lMax, cur);
if (cur > lMax) {
leftMaxIndex = i;
}
}
let rightMaxIndex = n - 1;
for (let i = n - 1; i >= 0; i--) {
const rMax = a[rightMaxIndex] + (rightMaxIndex - i);
const cur = a[i];
right[i] = Math.max(rMax, cur);
if (cur > rMax) {
rightMaxIndex = i;
}
}
let ans = Number.MAX_SAFE_INTEGER;
for (let i = 0; i < n; i++) {
ans = Math.min(ans, Math.max(left[i], right[i]));
}
console.log(ans);
});
Go
go
package main
import (
"bufio"
"fmt"
"math"
"os"
)
func main() {
in := bufio.NewReader(os.Stdin)
var n int
fmt.Fscan(in, &n)
a := make([]int64, n)
for i := 0; i < n; i++ {
fmt.Fscan(in, &a[i])
}
// 左侧包含本身所需最小功率 右侧包含本身所需最小功率
left := make([]int64, n)
right := make([]int64, n)
leftMaxIndex := 0
for i := 0; i < n; i++ {
lMax := a[leftMaxIndex] + int64(i-leftMaxIndex)
cur := a[i]
if lMax > cur {
left[i] = lMax
} else {
left[i] = cur
}
if cur > lMax {
leftMaxIndex = i
}
}
rightMaxIndex := n - 1
for i := n - 1; i >= 0; i-- {
rMax := a[rightMaxIndex] + int64(rightMaxIndex-i)
cur := a[i]
if rMax > cur {
right[i] = rMax
} else {
right[i] = cur
}
if cur > rMax {
rightMaxIndex = i
}
}
ans := int64(math.MaxInt64)
for i := 0; i < n; i++ {
cur := left[i]
if right[i] > cur {
cur = right[i]
}
if cur < ans {
ans = cur
}
}
fmt.Println(ans)
}