健身达人每日步数
题目分析
给定 天的步数记录,如果某天的步数严格大于其后某一天步数的两倍,则该天称为"突破日"。求突破日的总数。
换句话说,第 天是突破日当且仅当存在
使得
,等价于
。
思路
后缀最小值
对于每一天,我们需要判断它的步数是否严格大于后面所有天中最小步数的两倍。暴力做法对每个位置都扫描后面所有元素求最小值,时间复杂度 。
优化方法很直观:从右往左遍历,维护一个后缀最小值 suffix_min。对于当前位置 (
),如果
,则第
天是突破日。判断完成后,将
纳入后缀最小值的更新。
注意最后一天没有后续天,不可能是突破日。另外需要特判 的边界情况。
以样例 1 验证: ,从右往左:
:
,无后续天,跳过
:
,突破日,
:
,突破日,
:
,突破日,
:
?不满足严格大于,不是突破日
结果为 ,与预期一致。
复杂度
- 时间复杂度:
,一次遍历
- 空间复杂度:
,存储输入数组(如果用流式读入可以做到
)
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
scanf("%d", &n);
vector<int> a(n);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
int ans = 0;
int suffix_min = INT_MAX;
// 从右往左扫描,维护后缀最小值
for (int i = n - 1; i >= 0; i--) {
if (i < n - 1 && a[i] > 2 * suffix_min) {
ans++;
}
suffix_min = min(suffix_min, a[i]);
}
printf("%d\n", ans);
return 0;
}
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] a = new int[n];
for (int i = 0; i < n; i++) a[i] = sc.nextInt();
int ans = 0;
int suffixMin = Integer.MAX_VALUE;
for (int i = n - 1; i >= 0; i--) {
if (i < n - 1 && a[i] > 2 * suffixMin) {
ans++;
}
suffixMin = Math.min(suffixMin, a[i]);
}
System.out.println(ans);
}
}
import sys
def main():
data = sys.stdin.read().split()
n = int(data[0])
a = [int(data[i + 1]) for i in range(n)]
ans = 0
suffix_min = float('inf')
for i in range(n - 1, -1, -1):
if i < n - 1 and a[i] > 2 * suffix_min:
ans += 1
suffix_min = min(suffix_min, a[i])
print(ans)
if __name__ == '__main__':
main()
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin });
const lines = [];
rl.on('line', line => lines.push(line.trim()));
rl.on('close', () => {
const n = parseInt(lines[0]);
if (n <= 0) {
console.log(0);
return;
}
const a = lines[1].split(' ').map(Number);
let ans = 0;
let suffixMin = Infinity;
for (let i = n - 1; i >= 0; i--) {
if (i < n - 1 && a[i] > 2 * suffixMin) {
ans++;
}
suffixMin = Math.min(suffixMin, a[i]);
}
console.log(ans);
});

京公网安备 11010502036488号