2026-04-05:范围内总波动值Ⅰ。用go语言,给定两个整数 num1 和 num2,考虑它们之间所有的整数(包含端点),即区间 [num1, num2]。
对区间内的每个整数,把它的每一位数字看作一个“位置”。对某一位数字,判断它是否构成“峰”或“谷”:
若这一位数字 严格大于 它左右相邻的两位数字,则这位属于峰。
若这一位数字 严格小于 它左右相邻的两位数字,则这位属于谷。
数字的第一位和最后一位不允许被算作峰或谷(因为它们没有两侧相邻位)。
若该数字的位数少于 3(即没有足够左右对比),则它的“波动值”为 0。
于是,一个整数的波动值等于:该数中所有峰的数量加上所有谷的数量。
最后要求:把区间 [num1, num2] 内每个整数的波动值加总,返回这个总和。
1 <= num1 <= num2 <= 100000。
输入: num1 = 120, num2 = 130。
输出: 3。
解释:
在范围 [120, 130] 内:
120:中间数位 2 是峰,波动值 = 1。
121:中间数位 2 是峰,波动值 = 1。
130:中间数位 3 是峰,波动值 = 1。
范围内所有其他数字的波动值均为 0。
因此,总波动值为 1 + 1 + 1 = 3。
题目来自力扣3751。
一、代码整体执行流程
步骤1:入口函数初始化
- 程序从
main函数开始,输入区间num1=120、num2=130。 - 调用
totalWaviness(num1, num2),这是总结果的计算入口。
步骤2:区间差值计算(总波动值核心逻辑)
totalWaviness函数执行:- 计算
f(num2):0到130所有数字的总波动值; - 计算
f(num1-1):0到119所有数字的总波动值; - 最终结果 =
f(130) - f(119),直接得到区间[120,130]的总波动值。
- 计算
步骤3:单边界总波动值计算(f(n)函数)
以计算f(130)为例,执行totalWavinessWithBound(130):
- 基础判断:130>100,不直接返回0,进入数位DP计算;
- 获取数字位数:130是3位数,记为
m=3; - 计算位数因子:3位数的最高位因子是100(10²),用于拆分每一位数字;
- 初始化记忆化数组(memo):
- 用途:存储DP过程中重复计算的结果,避免重复递归,提升效率;
- 维度:
[位数][前一位数字][大小关系标记][是否受限],所有值初始化为-1(标记未计算);
- 启动递归DP:从数字的**最高位(第0位)**开始递归计算,返回0到n的总波动值。
步骤4:数位DP递归核心逻辑(dp函数)
这是计算波动值的核心,递归遍历数字的每一位,逐位枚举可能的数字,记录状态并累加波动值:
- 递归终止条件:遍历完所有位数(到达最后一位的下一位),返回:当前波动值=0,当前数字个数=1(代表1个完整数字);
- 状态读取:如果当前状态已经计算过(非受限状态),直接读取记忆化数组的结果,不重复计算;
- 确定当前位可枚举的最大数字:
- 若受限(tight=true):当前位不能超过原数字的对应位(如130最高位只能枚举1);
- 若不受限:当前位可以枚举0~9所有数字;
- 逐位枚举数字(0~最大数字):
- 记录当前位数字作为下一位的「前一位数字」;
- 计算新的大小关系:当前位数字 和 前一位数字 的大小对比(小于/等于/大于/初始未知);
- 更新受限状态:只有上一步受限且当前位取到最大值,下一步才继续受限;
- 递归下一位:计算后续所有位的总波动值和数字个数;
- 累加波动值:根据「前后大小关系的变化」判断是否新增峰/谷,每新增1个峰/谷,就给所有后续数字的波动值+1;
- 记忆化存储:将非受限状态的计算结果存入memo数组,方便后续复用;
- 返回结果:返回当前状态下的总波动值和数字总个数。
步骤5:大小关系判断辅助函数
getNewComparison:判断当前位数字和前一位数字的大小关系(小于/等于/大于),初始状态(无前一位)标记为未知;wavinessIncrease:判断是否新增峰/谷——只有前一次是小于、当前是大于 或 前一次是大于、当前是小于(严格交替),才会产生1个峰/谷,波动值+1;其余情况不增加。
步骤6:输出最终结果
f(130) - f(119) = 3,程序打印结果3,与题目示例一致。
二、关键细节补充
- 位数过滤:代码中
n≤100直接返回0,因为≤100的数字位数<3,波动值恒为0; - 记忆化优化:memo数组避免了大量重复递归,是数位DP高效的核心;
- 波动值统计规则:严格按照「中间位、严格大于/小于、峰谷交替」统计,首尾位不参与,完全符合题目要求。
三、时间复杂度分析
- 核心计算单元:数位DP的状态总数 = 位数 × 前一位数字(0~9) × 大小关系(4种) × 受限状态(2种);
- 最大位数:题目上限100000是6位数,因此总状态数 =
6 × 10 × 4 × 2 = 480(常数级); - 递归次数:每个状态仅计算1次,逐位枚举数字最多10次(常数);
- 总时间复杂度:O(1)(常数级)。 原因:数字最大固定为6位,所有计算量都是固定常数,与输入数字大小无关。
四、额外空间复杂度分析
额外空间指除输入输出外,程序运行中申请的内存:
- 主要空间:memo记忆化数组,大小为
6 × 10 × 4 × 2 = 480个int64元素; - 递归栈空间:最大递归深度=数字最大位数=6层;
- 其他变量:位数、因子、临时变量等,均为常数级;
- 总额外空间复杂度:O(1)(常数级)。
总结
- 执行过程:区间差值法拆分计算 → 数位DP逐位枚举数字 → 状态记忆化优化 → 辅助函数判断峰谷 → 累加总波动值;
- 时间复杂度:O(1)(常数级,固定计算量);
- 额外空间复杂度:O(1)(常数级,固定内存占用)。
Go完整代码如下:
package main
import (
"fmt"
)
const (
UNKNOWN = 0
LESS = 1
EQUAL = 2
GREATER = 3
)
func totalWaviness(num1 int64, num2 int64) int64 {
return totalWavinessWithBound(num2) - totalWavinessWithBound(num1-1)
}
func totalWavinessWithBound(n int64) int64 {
if n <= 100 {
return 0
}
m := getLength(n)
factor := int64(1)
for i := 1; i < m; i++ {
factor *= 10
}
// memo[position][prev][comparison][tight]
memo := make([][][][]int64, m)
for i := 0; i < m; i++ {
memo[i] = make([][][]int64, 10)
for j := 0; j < 10; j++ {
memo[i][j] = make([][]int64, 4)
for k := 0; k < 4; k++ {
memo[i][j][k] = []int64{-1, -1}
}
}
}
res, _ := dp(memo, n, factor, 0, 0, UNKNOWN, true)
return res
}
func getLength(n int64) int {
length := 0
for n > 0 {
n /= 10
length++
}
return length
}
func dp(memo [][][][]int64, n int64, factor int64, position int, prev int, comparison int, tight bool) (int64, int64) {
if position == len(memo) {
return 0, 1
}
tightFlag := 0
if !tight {
tightFlag = 1
}
if tightFlag == 1 && memo[position][prev][comparison][0] >= 0 {
return memo[position][prev][comparison][0], memo[position][prev][comparison][1]
}
var newWaviness int64 = 0
var newCount int64 = 0
digit := int((n / factor) % 10)
maxDigit := digit
if !tight {
maxDigit = 9
}
for d := 0; d <= maxDigit; d++ {
newPrev := d
newComparison := getNewComparison(d, prev, comparison)
newTight := tight && d == digit
nextWav, nextCount := dp(memo, n, factor/10, position+1, newPrev, newComparison, newTight)
newWaviness += nextWav + int64(wavinessIncrease(comparison, newComparison))*nextCount
newCount += nextCount
}
if tightFlag == 1 {
memo[position][prev][comparison][0] = newWaviness
memo[position][prev][comparison][1] = newCount
}
return newWaviness, newCount
}
func getNewComparison(curr int, prev int, comparison int) int {
if comparison == UNKNOWN && prev == 0 {
return UNKNOWN
}
if curr < prev {
return LESS
} else if curr == prev {
return EQUAL
} else {
return GREATER
}
}
func wavinessIncrease(comparison int, newComparison int) int {
if (comparison == LESS && newComparison == GREATER) || (comparison == GREATER && newComparison == LESS) {
return 1
}
return 0
}
func main() {
num1 := int64(120)
num2 := int64(130)
result := totalWaviness(num1, num2)
fmt.Println(result)
}
Python完整代码如下:
# -*-coding:utf-8-*-
class Solution:
UNKNOWN = 0
LESS = 1
EQUAL = 2
GREATER = 3
def totalWaviness(self, num1: int, num2: int) -> int:
return self.totalWavinessWithBound(num2) - self.totalWavinessWithBound(num1 - 1)
def totalWavinessWithBound(self, n: int) -> int:
if n <= 100:
return 0
m = self.getLength(n)
factor = 10 ** (m - 1)
# memo[position][prev][comparison] = [waviness, count] for non-tight states
memo = [[[[-1, -1] for _ in range(4)] for _ in range(10)] for _ in range(m)]
res, _ = self.dp(memo, n, factor, 0, 0, self.UNKNOWN, True)
return res
def getLength(self, n: int) -> int:
length = 0
while n > 0:
n //= 10
length += 1
return length
def dp(self, memo, n: int, factor: int, position: int, prev: int, comparison: int, tight: bool):
if position == len(memo):
return 0, 1
if not tight and memo[position][prev][comparison][0] >= 0:
return memo[position][prev][comparison][0], memo[position][prev][comparison][1]
newWaviness = 0
newCount = 0
digit = (n // factor) % 10
maxDigit = digit if tight else 9
for d in range(maxDigit + 1):
newPrev = d
newComparison = self.getNewComparison(d, prev, comparison)
newTight = tight and (d == digit)
nextWav, nextCount = self.dp(memo, n, factor // 10, position + 1, newPrev, newComparison, newTight)
newWaviness += nextWav + self.wavinessIncrease(comparison, newComparison) * nextCount
newCount += nextCount
if not tight:
memo[position][prev][comparison][0] = newWaviness
memo[position][prev][comparison][1] = newCount
return newWaviness, newCount
def getNewComparison(self, curr: int, prev: int, comparison: int) -> int:
if comparison == self.UNKNOWN and prev == 0:
return self.UNKNOWN
if curr < prev:
return self.LESS
elif curr == prev:
return self.EQUAL
else:
return self.GREATER
def wavinessIncrease(self, comparison: int, newComparison: int) -> int:
if (comparison == self.LESS and newComparison == self.GREATER) or \
(comparison == self.GREATER and newComparison == self.LESS):
return 1
return 0
def main():
sol = Solution()
num1 = 120
num2 = 130
result = sol.totalWaviness(num1, num2)
print(result)
if __name__ == "__main__":
main()
C++完整代码如下:
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
const int UNKNOWN = 0;
const int LESS = 1;
const int EQUAL = 2;
const int GREATER = 3;
int getLength(long long n) {
int length = 0;
while (n > 0) {
n /= 10;
length++;
}
return length;
}
int getNewComparison(int curr, int prev, int comparison) {
if (comparison == UNKNOWN && prev == 0) {
return UNKNOWN;
}
if (curr < prev) {
return LESS;
} else if (curr == prev) {
return EQUAL;
} else {
return GREATER;
}
}
int wavinessIncrease(int comparison, int newComparison) {
if ((comparison == LESS && newComparison == GREATER) ||
(comparison == GREATER && newComparison == LESS)) {
return 1;
}
return 0;
}
// memo[position][prev][comparison][0] = waviness, [1] = count
vector<vector<vector<vector<long long>>>> memo;
pair<long long, long long> dp(long long n, long long factor, int position, int prev, int comparison, bool tight) {
if (position == memo.size()) {
return {0, 1};
}
int tightFlag = tight ? 0 : 1;
if (!tight && memo[position][prev][comparison][0] >= 0) {
return {memo[position][prev][comparison][0], memo[position][prev][comparison][1]};
}
long long newWaviness = 0;
long long newCount = 0;
int digit = (n / factor) % 10;
int maxDigit = tight ? digit : 9;
for (int d = 0; d <= maxDigit; d++) {
int newPrev = d;
int newComparison = getNewComparison(d, prev, comparison);
bool newTight = tight && (d == digit);
auto [nextWav, nextCount] = dp(n, factor / 10, position + 1, newPrev, newComparison, newTight);
newWaviness += nextWav + (long long)wavinessIncrease(comparison, newComparison) * nextCount;
newCount += nextCount;
}
if (!tight) {
memo[position][prev][comparison][0] = newWaviness;
memo[position][prev][comparison][1] = newCount;
}
return {newWaviness, newCount};
}
long long totalWavinessWithBound(long long n) {
if (n <= 100) {
return 0;
}
int m = getLength(n);
long long factor = 1;
for (int i = 1; i < m; i++) {
factor *= 10;
}
// 初始化 memo
memo.assign(m, vector<vector<vector<long long>>>(
10, vector<vector<long long>>(
4, vector<long long>(2, -1))));
auto [res, _] = dp(n, factor, 0, 0, UNKNOWN, true);
return res;
}
long long totalWaviness(long long num1, long long num2) {
return totalWavinessWithBound(num2) - totalWavinessWithBound(num1 - 1);
}
int main() {
long long num1 = 120;
long long num2 = 130;
long long result = totalWaviness(num1, num2);
cout << result << endl;
return 0;
}