普通视图

发现新文章,点击刷新页面。
今天 — 2026年3月13日首页

每日一题-移山所需的最少秒数🟡

2026年3月13日 00:00

给你一个整数 mountainHeight 表示山的高度。

同时给你一个整数数组 workerTimes,表示工人们的工作时间(单位:)。

工人们需要 同时 进行工作以 降低 山的高度。对于工人 i :

  • 山的高度降低 x,需要花费 workerTimes[i] + workerTimes[i] * 2 + ... + workerTimes[i] * x 秒。例如:
    • 山的高度降低 1,需要 workerTimes[i] 秒。
    • 山的高度降低 2,需要 workerTimes[i] + workerTimes[i] * 2 秒,依此类推。

返回一个整数,表示工人们使山的高度降低到 0 所需的 最少 秒数。

 

示例 1:

输入: mountainHeight = 4, workerTimes = [2,1,1]

输出: 3

解释:

将山的高度降低到 0 的一种方式是:

  • 工人 0 将高度降低 1,花费 workerTimes[0] = 2 秒。
  • 工人 1 将高度降低 2,花费 workerTimes[1] + workerTimes[1] * 2 = 3 秒。
  • 工人 2 将高度降低 1,花费 workerTimes[2] = 1 秒。

因为工人同时工作,所需的最少时间为 max(2, 3, 1) = 3 秒。

示例 2:

输入: mountainHeight = 10, workerTimes = [3,2,2,4]

输出: 12

解释:

  • 工人 0 将高度降低 2,花费 workerTimes[0] + workerTimes[0] * 2 = 9 秒。
  • 工人 1 将高度降低 3,花费 workerTimes[1] + workerTimes[1] * 2 + workerTimes[1] * 3 = 12 秒。
  • 工人 2 将高度降低 3,花费 workerTimes[2] + workerTimes[2] * 2 + workerTimes[2] * 3 = 12 秒。
  • 工人 3 将高度降低 2,花费 workerTimes[3] + workerTimes[3] * 2 = 12 秒。

所需的最少时间为 max(9, 12, 12, 12) = 12 秒。

示例 3:

输入: mountainHeight = 5, workerTimes = [1]

输出: 15

解释:

这个示例中只有一个工人,所以答案是 workerTimes[0] + workerTimes[0] * 2 + workerTimes[0] * 3 + workerTimes[0] * 4 + workerTimes[0] * 5 = 15 秒。

 

提示:

  • 1 <= mountainHeight <= 105
  • 1 <= workerTimes.length <= 104
  • 1 <= workerTimes[i] <= 106

二分

作者 tsreaper
2024年9月22日 12:15

解法:二分

二分答案,并计算每个工人在当前二分的值下能降低多少高度。这个计算也可以通过二分高度来进行。

最差情况是只有一个工人,且 workerTimes[0] = 1e6,所以二分的上界就是 $10^6 \times \frac{(1 + 10^5) \times 10^5}{2} \approx 10^{16}$。

复杂度 $\mathcal{O}(n\log h \log t)$,其中 $h = 10^5$ 是山的高度上限,$t = 10^{16}$ 是二分上限。

参考代码(c++)

###cpp

class Solution {
public:
    long long minNumberOfSeconds(int mountainHeight, vector<int>& workerTimes) {
        // 通过二分,计算每个工人在 lim 的时间内能降低多少高度
        auto calc = [&](long long lim) {
            int ret = 0;
            for (int t : workerTimes) {
                int head = 0, tail = mountainHeight;
                while (head < tail) {
                    int mid = (head + tail + 1) >> 1;
                    if (1LL * (1 + mid) * mid / 2 * t <= lim) head = mid;
                    else tail = mid - 1;
                }
                ret += head;
                // 提前退出,防止结果溢出
                if (ret >= mountainHeight) break;
            }
            return ret;
        };

        // 二分总用时
        long long head = 1, tail = 1e18;
        while (head < tail) {
            long long mid = (head + tail) >> 1;
            if (calc(mid) >= mountainHeight) tail = mid;
            else head = mid + 1;
        }
        return head;
    }
};

两种方法:最小堆模拟/二分答案(Python/Java/C++/Go)

作者 endlesscheng
2024年9月22日 12:08

方法一:最小堆模拟

循环 $\textit{mountainHeight}$ 次,每次选一个「工作后总用时」最短的工人,把山的高度降低 $1$。

具体请看 视频讲解,欢迎点赞关注~

###py

class Solution:
    def minNumberOfSeconds(self, mountainHeight: int, workerTimes: List[int]) -> int:
        h = [(t, t, t) for t in workerTimes]
        heapify(h)
        for _ in range(mountainHeight):
            # 工作后总用时,当前工作(山高度降低 1)用时,workerTimes[i]
            nxt, delta, base = h[0]
            heapreplace(h, (nxt + delta + base, delta + base, base))
        return nxt  # 最后一个出堆的 nxt 即为答案

###java

class Solution {
    public long minNumberOfSeconds(int mountainHeight, int[] workerTimes) {
        PriorityQueue<long[]> pq = new PriorityQueue<>((a, b) -> Long.compare(a[0], b[0]));
        for (int t : workerTimes) {
            pq.offer(new long[]{t, t, t});
        }
        long ans = 0;
        while (mountainHeight-- > 0) {
            // 工作后总用时,当前工作(山高度降低 1)用时,workerTimes[i]
            long[] w = pq.poll();
            long nxt = w[0], delta = w[1], base = w[2];
            ans = nxt; // 最后一个出堆的 nxt 即为答案
            pq.offer(new long[]{nxt + delta + base, delta + base, base});
        }
        return ans;
    }
}

###cpp

class Solution {
public:
    long long minNumberOfSeconds(int mountainHeight, vector<int>& workerTimes) {
        priority_queue<tuple<long long, long long, int>, vector<tuple<long long, long long, int>>, greater<>> pq;
        for (int t : workerTimes) {
            pq.emplace(t, t, t);
        }
        long long ans = 0;
        while (mountainHeight--) {
            // 工作后总用时,当前工作(山高度降低 1)用时,workerTimes[i]
            auto [nxt, delta, base] = pq.top(); pq.pop();
            ans = nxt; // 最后一个出堆的 nxt 即为答案
            pq.emplace(nxt + delta + base, delta + base, base);
        }
        return ans;
    }
};

###go

func minNumberOfSeconds(mountainHeight int, workerTimes []int) int64 {
h := make(hp, len(workerTimes))
for i, t := range workerTimes {
h[i] = worker{t, t, t}
}
heap.Init(&h)

ans := 0
for ; mountainHeight > 0; mountainHeight-- {
ans = h[0].nxt // 最后一个出堆的 nxt 即为答案
h[0].delta += h[0].base
h[0].nxt += h[0].delta
heap.Fix(&h, 0)
}
return int64(ans)
}

// 工作后总用时,当前工作(山高度降低 1)用时,workerTimes[i]
type worker struct{ nxt, delta, base int }
type hp []worker
func (h hp) Len() int           { return len(h) }
func (h hp) Less(i, j int) bool { return h[i].nxt < h[j].nxt }
func (h hp) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }
func (hp) Push(any)             {}
func (hp) Pop() (_ any)         { return }

复杂度分析

  • 时间复杂度:$\mathcal{O}(\textit{mountainHeight}\log n)$,其中 $n$ 是 $\textit{workerTimes}$ 的长度。
  • 空间复杂度:$\mathcal{O}(n)$。

方法二:二分答案

由于花的时间越多,能够降低的高度也越多,所以有单调性,可以二分答案。

问题变成:

  • 每个工人至多花费 $m$ 秒,总共降低的高度是多少?能否大于等于 $\textit{mountainHeight}$?

遍历 $\textit{workerTimes}$,设 $t=\textit{workerTimes}[i]$,那么有

$$
t + 2t+ \cdots + xt = t\cdot \dfrac{x(x+1)}{2} \le m
$$

$$
\dfrac{x(x+1)}{2} \le \left\lfloor\dfrac{m}{t}\right\rfloor = k
$$

解得

$$
x \le \dfrac{-1 + \sqrt{1 + 8k}}{2}
$$

所以第 $i$ 名工人可以把山的高度降低

$$
\left\lfloor \dfrac{-1 + \sqrt{1 + 8k}}{2} \right\rfloor = \left\lfloor \dfrac{-1 + \lfloor\sqrt{1 + 8k}\rfloor}{2} \right\rfloor
$$

累加上式,如果和 $\ge \textit{mountainHeight}$,则说明答案 $\le m$,否则说明答案 $> m$。

最后,讨论二分的上下界。这里用开区间二分,其他二分写法也是可以的。

  • 开区间二分下界:$0$,无法把山的高度降低到 $0$。
  • 开区间二分上界:设 $\textit{maxT}$ 为 $\textit{workerTimes}$ 的最大值,假设每个工人都是最慢的 $\textit{maxT}$,那么单个工人要把山降低 $h=\left\lceil\dfrac{mountainHeight}{n}\right\rceil$,耗时 $\textit{maxT}\cdot(1+2+\cdots+h)=\textit{maxT}\cdot\dfrac{h(h+1)}{2}$,将其作为开区间的二分上界,一定可以把山的高度降低到 $\le 0$。

关于上取整的计算,当 $a$ 和 $b$ 均为正整数时,我们有

$$
\left\lceil\dfrac{a}{b}\right\rceil = \left\lfloor\dfrac{a-1}{b}\right\rfloor + 1
$$

证明见 上取整下取整转换公式的证明

###py

class Solution:
    def minNumberOfSeconds(self, mountainHeight: int, workerTimes: List[int]) -> int:
        def check(m: int) -> bool:
            left_h = mountainHeight
            for t in workerTimes:
                left_h -= (isqrt(m // t * 8 + 1) - 1) // 2
                if left_h <= 0:
                    return True
            return False

        max_t = max(workerTimes)
        h = (mountainHeight - 1) // len(workerTimes) + 1
        return bisect_left(range(max_t * h * (h + 1) // 2), True, 1, key=check)

###py

class Solution:
    def minNumberOfSeconds(self, mountainHeight: int, workerTimes: List[int]) -> int:
        f = lambda m: sum((isqrt(m // t * 8 + 1) - 1) // 2 for t in workerTimes)
        max_t = max(workerTimes)
        h = (mountainHeight - 1) // len(workerTimes) + 1
        return bisect_left(range(max_t * h * (h + 1) // 2), mountainHeight, 1, key=f)

###java

class Solution {
    public long minNumberOfSeconds(int mountainHeight, int[] workerTimes) {
        int maxT = 0;
        for (int t : workerTimes) {
            maxT = Math.max(maxT, t);
        }
        int h = (mountainHeight - 1) / workerTimes.length + 1;
        long left = 0;
        long right = (long) maxT * h * (h + 1) / 2;
        while (left + 1 < right) {
            long mid = (left + right) / 2;
            if (check(mid, mountainHeight, workerTimes)) {
                right = mid;
            } else {
                left = mid;
            }
        }
        return right;
    }

    private boolean check(long m, int leftH, int[] workerTimes) {
        for (int t : workerTimes) {
            leftH -= ((int) Math.sqrt(m / t * 8 + 1) - 1) / 2;
            if (leftH <= 0) {
                return true;
            }
        }
        return false;
    }
}

###cpp

class Solution {
public:
    long long minNumberOfSeconds(int mountainHeight, vector<int>& workerTimes) {
        auto check = [&](long long m) {
            int left_h = mountainHeight;
            for (int t : workerTimes) {
                left_h -= ((int) sqrt(m / t * 8 + 1) - 1) / 2;
                if (left_h <= 0) {
                    return true;
                }
            }
            return false;
        };

        int max_t = ranges::max(workerTimes);
        int h = (mountainHeight - 1) / workerTimes.size() + 1;
        long long left = 0, right = (long long) max_t * h * (h + 1) / 2;
        while (left + 1 < right) {
            long long mid = (left + right) / 2;
            (check(mid) ? right : left) = mid;
        }
        return right;
    }
};

###go

func minNumberOfSeconds(mountainHeight int, workerTimes []int) int64 {
maxT := slices.Max(workerTimes)
h := (mountainHeight-1)/len(workerTimes) + 1
ans := 1 + sort.Search(maxT*h*(h+1)/2-1, func(m int) bool {
m++
leftH := mountainHeight
for _, t := range workerTimes {
leftH -= (int(math.Sqrt(float64(m/t*8+1))) - 1) / 2
if leftH <= 0 {
return true
}
}
return false
})
return int64(ans)
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(n\log U)$,其中 $n$ 是 $\textit{workerTimes}$ 的长度,$U\le 5\cdot 10^{10}(10^5+1)$ 是二分上界。二分 $\mathcal{O}(\log U)$ 次,每次 $\mathcal{O}(n)$ 时间。开平方有专门的 CPU 指令,可以视作 $\mathcal{O}(1)$。
  • 空间复杂度:$\mathcal{O}(1)$。

专题训练

见下面二分题单中的「二分答案:求最小」。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、二叉树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA/一般树)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

昨天 — 2026年3月12日首页

JavaScript 对象与属性描述符:从原理到实战

作者 swipe
2026年3月12日 22:50

背景:为什么要深入理解对象?

在日常开发中,我们经常会遇到这样的困惑:

  • 为什么有些对象属性用 for-in 遍历不出来?
  • 为什么 delete 有时能删除属性,有时却失效?
  • Vue2 的响应式原理到底是怎么"劫持"属性访问的?

这些问题的答案都指向同一个核心概念:属性描述符。它是 JavaScript 对象系统的底层机制,掌握它不仅能让你理解框架源码,还能写出更精准、更可控的代码。

本文将从面向对象的本质出发,逐步深入到属性描述符的细节,并结合实际场景帮你建立完整的知识体系。

你将收获:

  • 理解 JavaScript 面向对象的设计思想
  • 掌握属性描述符的 6 种特性及应用场景
  • 学会用 Object.defineProperty 精准控制对象行为
  • 具备阅读 MDN 文档和框架源码的基础能力

一、面向对象:用代码模拟现实世界

1.1 什么是面向对象?

面向对象编程(OOP)的核心思想是:用包含数据和行为的对象来模拟现实世界的实体

举个例子:

  • 一辆车(Car):有颜色、速度、品牌、价格等属性,有行驶、刹车等方法
  • 一个人(Person):有姓名、年龄、身高等属性,有吃饭、跑步等方法

这种抽象方式让代码结构更清晰,也更贴近人类的思维方式。在 JavaScript 中,面向对象主要体现在两个方面:

  1. 封装:把相关数据和方法组织在一起(函数、模块、对象都是封装)
  2. 继承:通过原型链实现代码复用(这是 JS 的重点,后续会详细讲解)

1.2 JavaScript 中的对象设计

JavaScript 支持多种编程范式,对象被设计成属性的无序集合,类似哈希表:

{
  key: value
}
  • key:标识符名称(字符串或 Symbol)
  • value:任意类型(基本类型、对象、函数等)
  • 如果 value 是函数,我们称之为方法

1.3 创建对象的两种方式

方式一:new Object()(构造函数方式)

var person1 = new Object();
person1.name = "小吴";
person1.age = 18;
person1.greet = function() {
  console.log("Hello, my name is " + this.name + " and I am " + this.age + " years old.");
};

person1.greet();  // 输出: Hello, my name is 小吴 and I am 18 years old.

适用场景:

  • 需要动态添加属性的复杂逻辑
  • 有 Java/C++ 等面向对象语言背景的开发者

历史背景: JavaScript 早期为了蹭 Java 的热度,在命名和语法上刻意模仿,导致很多 Java 开发者习惯用这种方式。

方式二:对象字面量(推荐)

var person2 = {
  name: "小吴",
  age: 18,
  greet: function() {
    console.log("Hello, my name is " + this.name + " and I am " + this.age + " years old.");
  }
};

person2.greet();  // 输出: Hello, my name is 小吴 and I am 18 years old.

优势:

  • 代码简洁,结构清晰
  • 属性和方法内聚性强
  • 性能略优(省略函数调用开销)

二、属性描述符:精准控制对象行为

2.1 为什么需要属性描述符?

通常我们直接定义属性:

var obj = {
  name: "小吴",
  age: 20,
  sex: "男"
};

// 获取属性
console.log(obj.name);  // 小吴

// 修改属性
obj.name = "XiaoWu";
console.log(obj.name);  // XiaoWu

// 删除属性
delete obj.name;
console.log(obj);  // { age: 20, sex: '男' }

但这种方式无法控制:

  • 这个属性能否被 delete 删除?
  • 这个属性能否被 for-in 遍历?
  • 这个属性能否被重新赋值?

属性描述符就是用来解决这些问题的工具。

2.2 Object.defineProperty 基础用法

Object.defineProperty(obj, prop, descriptor)

参数说明:

  • obj:目标对象
  • prop:属性名(字符串或 Symbol)
  • descriptor:属性描述符对象(核心)

返回值: 修改后的原对象(非纯函数)

示例:

var obj = {
  name: "XiaoWu",
  age: 20
};

Object.defineProperty(obj, "height", {
  value: 1.75
});

console.log(obj);  // Node 环境:{ name: 'XiaoWu', age: 20 }

疑问:为什么 height 没显示出来?

图 1:浏览器控制台显示了 height 属性

原因分析:

  • height 默认是不可枚举的(enumerable: false
  • Node.jsconsole.log 使用 util.inspect(),默认只显示可枚举属性(遵循 ECMAScript 标准)
  • 浏览器控制台 为了调试方便,会显示所有属性(包括不可枚举属性)

验证属性确实存在:

console.log(obj.height);  // 1.75(可以访问)

让属性可枚举:

Object.defineProperty(obj, "height", {
  value: 1.75,
  enumerable: true  // 设置为可枚举
});

console.log(obj);  // { name: 'XiaoWu', age: 20, height: 1.75 }

三、属性描述符的两种类型

属性描述符分为两大类,它们不能混用

类型 configurable enumerable value writable get set
数据描述符
存取描述符

记忆口诀: 2 共用 + 2 可选,同时生效最多 4 种

3.1 为什么不能混用?

本质原因: 它们代表了两种完全不同的属性管理方式

  • 数据描述符(静态):属性持有一个具体的值,可以直接读写
  • 存取描述符(动态):属性值通过函数动态计算,每次访问可能不同

如果同时定义,JavaScript 引擎无法判断应该直接操作值还是调用函数,因此规范禁止混用。

类比理解:

  • 数据描述符 = 名词(静态的"数据")
  • 存取描述符 = 动词(动态的"存取"操作)

四、数据描述符详解

4.1 四大特性

[[Configurable]]:可配置性

控制属性是否可以:

  • delete 删除
  • 修改其他描述符特性
  • 转换为存取描述符

默认值:

  • 直接定义:true
  • 通过描述符定义:false

[[Enumerable]]:可枚举性

控制属性是否可以:

  • for-in 遍历
  • Object.keys() 返回

默认值:

  • 直接定义:true
  • 通过描述符定义:false

[[Writable]]:可写性

控制属性值是否可以被修改。

默认值:

  • 直接定义:true
  • 通过描述符定义:false

[[Value]]:属性值

属性的实际值。

默认值: undefined

4.2 实战案例

var obj = {
  name: "XiaoWu",
  age: 18
};

// 定义一个受控属性
Object.defineProperty(obj, "address", {
  value: "福建省",
  configurable: false,  // 不可删除、不可重新配置
  enumerable: true,     // 可枚举
  writable: false       // 不可修改
});

// 测试 configurable
delete obj.name;
console.log(obj);  // { age: 18, address: '福建省' }(name 被删除)

delete obj.address;
console.log(obj.address);  // 福建省(删除失败)

// 测试 enumerable
console.log(Object.keys(obj));  // [ 'age', 'address' ]

for (var key in obj) {
  console.log(key);  // age, address
}

// 测试 writable
obj.address = "上海市";
console.log(obj.address);  // 福建省(修改失败)

关键点:

  • 直接定义的属性(nameage)默认所有特性都是 true
  • 通过描述符定义的属性(address)默认所有特性都是 false

五、存取描述符详解

5.1 四大特性

  • [[Configurable]]:同数据描述符
  • [[Enumerable]]:同数据描述符
  • [[Get]]:获取属性时执行的函数,默认 undefined
  • [[Set]]:设置属性时执行的函数,默认 undefined

5.2 应用场景

场景一:隐藏私有属性

var obj = {
  name: "小吴",
  age: 18,
  _address: "泉州市"  // _ 开头表示私有属性(约定俗成)
};

Object.defineProperty(obj, "address", {
  enumerable: true,
  configurable: true,
  get: function() {
    return this._address;  // 通过 address 访问 _address
  },
  set: function(value) {
    this._address = value;
  }
});

console.log(obj.address);  // 泉州市
obj.address = "厦门市";
console.log(obj.address);  // 厦门市

注意: ES6 后可以用 # 定义真正的私有属性(后续会讲)。

场景二:拦截属性访问(Vue2 响应式原理)

var obj = {
  name: "小吴",
  age: 18,
  _address: "泉州市"
};

Object.defineProperty(obj, "address", {
  enumerable: true,
  configurable: true,
  get: function() {
    console.log("获取了一次 address 的值");  // 拦截读取
    return this._address;
  },
  set: function(value) {
    console.log("设置了一次 address 的值");  // 拦截写入
    this._address = value;
  }
});

console.log(obj.address);
// 输出:获取了一次 address 的值
//      泉州市

obj.address = "why";
// 输出:设置了一次 address 的值

console.log(obj.address);
// 输出:获取了一次 address 的值
//      why

核心价值: 这就是 Vue2 响应式系统的底层原理——通过 get/set 拦截属性访问,实现依赖收集和派发更新。


六、学习属性描述符的实战意义

6.1 理解原生 API 的能力边界

所有原生对象的 API 都有属性描述符,这决定了它们的行为:

  • 为什么 Array.prototype 上的方法用 for-in 遍历不出来?(enumerable: false
  • 为什么 Object.prototype.toString 不能被删除?(configurable: false

6.2 读懂技术文档

MDN 文档中大量使用属性描述符来描述 API 特性:

图 2:MDN 文档对 API 能力边界的描述

掌握这些概念后,你能:

  • 快速理解 API 的使用限制
  • 预判代码的行为边界
  • 避免踩坑(比如误删不可配置的属性)

6.3 降低框架学习门槛

React、Vue 等框架文档中会用到这些术语:

图 3:React 文档中的专业术语

学完 JavaScript 高级后,这些词汇对你来说将不再陌生。


七、关键要点总结

  1. 属性描述符分两类:数据描述符(静态值)和存取描述符(动态函数),不能混用
  2. 默认值差异:直接定义的属性默认可配置/可枚举/可写,通过描述符定义的默认都是 false
  3. 核心应用场景
    • 隐藏私有属性(用 get/set 代理访问)
    • 拦截属性访问(实现响应式、日志、校验等)
    • 精准控制对象行为(防删除、防修改、防遍历)
  4. 实战价值:理解原生 API、读懂技术文档、掌握框架原理

八、下一步建议

团队落地建议:

  • 在工具函数库中封装常用的属性控制逻辑(如冻结对象、只读属性等)
  • Code Review 时关注属性描述符的使用是否合理
  • 在复杂对象设计中主动使用描述符提升代码健壮性

后续学习方向:

  • 批量定义属性描述符(Object.defineProperties
  • 对象方法补充(Object.freezeObject.seal 等)
  • 工厂函数与构造函数
  • 原型链与继承机制

下一篇我们将深入构造函数,探索更高效的对象创建方案。

云端养虾不花钱?阶跃 StepClaw 来真的

作者 杜晨
2026年3月12日 22:14

最近想必不少人都装上了「龙虾」,还没真的干什么活,先收到了一张账单。

金额从几十到几百不等,有人甚至看到四、五位数。

OpenClaw 是个 token 销金窟, 懂行的人多少都清楚。仅仅是将龙虾配置成自己需要的样子,都要消耗非常多 token,配置好了之后日常运转 + 对话,一天耗费量也不少——它在后台搜资料、写代码、调接口、生成文件,每一个动作都在烧。

账单是实时在跑的,只是很多人没意识到。

目前市场上一窝蜂出现的一键部署工具,有的会告诉你按量计费,没良心的先把你拉上车,想下车先交钱,管杀不管埋。

而今天,阶跃往前多走了一步。

APPSO 注意到,阶跃星辰「阶跃 AIAPP上线了 StepClaw

阶跃提供了 5 万个首批名额,全包式免费,先到先得。

这个免费是字面意义上的:5000 万模型 Tokens、云服务器费用、存储费用,全包,没有隐藏的按量扣费。

尽管天下没有一直免费的午餐,这个免费使用限时 1 个月,但阶跃给的 Token 额度,足够你配置好自己的龙虾,并摸索、使用很长一段时间了。除了 APP 端以外,StepClaw 明天还将在阶跃 AI 网页端上线,支持部署和使用。

StepClaw 怎么玩?

很简单,只需要去应用商城下载「阶跃 AI」就可以了。

下载登录后,你会在左下角发现一个「StepClaw」字样——这就是「阶跃龙虾」的入口。之后再进来跟龙虾聊天,点这里就好了。

初次配置的话,过程说是需要三分钟,APPSO 实测一分钟不到就搞完了。

根据阶跃官方介绍,StepClaw 的云电脑配置为双核 CPU、4GB 内存和 40GB 存储。作为 OpenClaw 的标准实例,该有的功能,例如复杂任务、长期记忆、全时在线等,全都有。

在这个页面你也会看到,阶跃会给首批用户赠送最多 5000 万 token 用于「阶跃龙虾」。

如果你之前从未使用过任何形式的 OpenClaw,或者想用阶跃 AI app 直接重新配置一个,那么就点击这个「一键配置使用」就好。

接下来看看能用「阶跃龙虾」做什么。

一个最经典的用法:每天早上给你出一份指定行业的早报,重点检测某几个网站。

只需要跟他说就行了。

在 APPSO 的测试中,经过大约两分钟左右,StepClaw 直接完成了第一个任务。

从返回的结果来看,它创建了一个脚本,使用 search 功能,并且调用系统事件来设置推送。对于定时功能,StepClaw 使用的也是 cron job 这一比较通用的逻辑。

这是 APPSO 本次部署后的第一次对话,由于 StepClaw 会在后台根据你的命令去确认必要的技能和工具,如果没有相应工具的话则会去自行寻找和安装——所以首次对话的反应比较慢,属于正常现象。

之后的话,StepClaw 会在云电脑上保存一份长期记忆,对于类似的任务,规划和执行速度都会变得更快。

作为一个标准化的 OpenClaw 实例,StepClaw 也是支持绝大多数 OpenClaw 能力的,包括和外部聊天机器人对接。

我起了一个 StepClaw 新号,告诉它我要对接飞书机器人(之前我并未配置过)。

这次它的反应速度倒是挺快,从头开始一步步教我,事无巨细倾囊相授……对于技术小白来说,还是很友好的。

这年头大家对龙虾应该已经不能更熟悉了,各有各的用法,多的我们就不赘述了。

目前 StepClaw 在阶跃 AI APP 端同时支持 iOS 和 Android 系统。5 万个免费名额,先到先得,发完即止。

虽然是免费的午餐,但只持续一个月。在这段时间里,云服务器、存储、5000 万模型 Tokens 相关成本全包。

至于什么时候、如何付费,目前还没有消息。所以大家可以趁此机会尝试一下,把 OpenClaw 调用榜单上登顶的大模型 Step 3.5 Flash,当成龙虾的「大脑」,究竟是一种怎样的感觉。

阶跃的龙虾棋谱

让我们回到刚才的开始配置界面,更有意思的还在下面:

在最底下,你还可以绑定已有的 OpenClaw,把你已经配置好的小龙虾带到阶跃上。

这么做有什么好处呢?APPSO 认为主要有两点:一是直接把阶跃 App 作为对话界面,方便手机操控,但又不用重新调教一遍龙虾;二是可以充分利用 Step 3.5 Flash,也就是阶跃目前最强的 Agent 基座模型。

和其它可用于小龙虾的模型相比,Step 3.5 Flash 有什么优势?一张图你就明白了。

这是 OpenRouter 上 OpenClaw 后端模型的使用量排名,在过去一个月时间里,Step 3.5 Flash 连续登顶小龙虾调用量日榜,同时也稳稳拿住月榜头把交椅:

在 OpenClaw 热火朝天的这两个月里,很多小白玩家最头疼的点,莫过于东西装好了,模型却选了个笨蛋。

模型这东西,大体上是便宜没好货。便宜的模型一般小,智力、规划、推理能力不行,没有面向 Agent 调用做过优化,用来当 OpenClaw 的脑子,经常会发生幻觉。

而 OpenClaw 的幻觉,跟一般大模型产品还不一样:

大模型最多给你吐出一些错乱的回答,可是一旦 OpenClaw 出现了幻觉,是真有可能错误地执行任务,甚至删掉重要文件的。

此时阶跃站出来了,送上免费的 Step 3.5 Flash 给普通用户和开发者使用。

能给出这个方案,阶跃下了一盘什么棋?

我们不妨复盘下 Kimi 最近的经历。

OpenClaw 爆火初期,「龙虾之父」Peter Steinberger 在采访中推荐了 MiniMax 的模型;但到了 2026 年 2 月,Kimi K2.5 因为便宜且 Agent 优化好,一度成为 OpenRouter 上 OpenClaw 调用量最高的模型。

OpenClaw 官方随即宣布 Kimi K2.5 为首个免费开放的主力模型,月之暗面同步为社区用户提供免费调用额度——两家联手,直接破圈了:

虽然 OpenClaw 免费,但收费的 K2.5 能力被整个商业世界看到。发布不到一个月,月之暗面宣称近 20 天的累计收入就超过了 2025 年全年总收入。海外收入首次超过国内,Kimi 月访问量达到 3300 万。

这说明一件事:在 OpenClaw 的生态里,谁的模型成为默认选择,谁就吃到最大的增长。

Step 3.5 Flash 是阶跃今年 2 月初发布的旗舰 Agent 基座模型,采用稀疏 MoE 架构,仅凭 11B 激活参数(总规模 196B MoE)便达到了顶尖级别的智能水平,最高推理速度达每秒 350 tokens,专门为 Agent 场景优化,在多个 Agent 基准测试上进了开源模型第一梯队。

Step 3.5 Flash上线两天,登上 OpenRouter Trending 第一,随后在 OpenClaw 调用量榜上一路爬升,目前在 OpenClaw 全球大模型调用量日榜和月榜上均排名第一。

用付费模型跑一个活跃 Agent,月成本很容易上到几百块。对养不起虾的用户来说,Step 3.5 Flash 本来就很经济了,现在又加上了 StepClaw 一键部署,更轻松了。

和其它更复杂的龙虾部署方案相比,APPSO 发现 StepClaw 的一键部署体验很「无感」,不需要写代码,让小白也可以拥有自己的 AI 助理。

大模型「小龙」,纷纷转型「龙虾剑客」

养虾热潮里,入局的公司多得数不清。腾讯、百度、阿里、字节,各自推出了云端部署方案;小米在做手机端的系统级龙虾,华为、荣耀也在跟进。一时间「龙虾家族」壮大到十几个分支,让人眼花缭乱。

但如果仔细看,这些产品其实分属两种不同的逻辑。

一种是提供安装辅助和通用界面,让用户可以自选模型供应商,通过 API Key 等方式去直接调用。

这种思路,基本上就是对 OpenClaw 进行「套壳」,是一种相对功能更强、定制性强,但对普通用户友好度稍差的思路。

另一种玩法,是原厂深度绑定:自家模型驱动自家 Claw 产品,从模型到部署一条龙,用户拿到手就是调好的。走这条路的:月之暗面的 KimiClaw,和 MiniMax 的 MaxClaw。

今天阶跃上线 StepClaw,终于凑齐了国产龙虾「三剑客」。

有意思的地方在于,这三家都是国产大模型创业公司,它们各自的大模型在这波 OpenClaw 热潮当中,被开发者们很快发现适配度很高,进而模型调用量爆发最猛。

原因并不复杂:OpenClaw 本身只是框架,驱动它的是大模型,谁的模型好用、便宜,开发者就选谁。

本质上,「套壳」行为,只是给用户提供个场地,部分套壳选手「项庄舞剑」很热闹,但其实意在沛公,想的是赚云服务的钱。

而原厂模型公司,手里握着聪明好用的「虾脑」——在此基础上开发个壳出来,只是工程上的小问题。

养虾这件事,门槛一直在降,从一两个小时的手动折腾部署,再到今天 StepClaw 的一键免费入口,操作层面已经压得很低了。

目前 OpenClaw 仍然处于一个热火朝天但争议不断的阶段,而且这个争议同时存在于技术、政策、社会层面。

但不论怎样,OpenClaw 仍然属于一个现象级的事件。在它的身上,一些人可能只看到了争议,而另一些人却看到了未来。

有趣的事情,甚至范式跃迁级别的事件,后面只会越来越多。

#欢迎关注爱范儿官方微信公众号:爱范儿(微信号:ifanr),更多精彩内容第一时间为您奉上。

爱范儿 | 原文链接 · 查看评论 · 新浪微博


帝尔激光:拟发行H股股票并在港交所主板上市

2026年3月12日 20:44
36氪获悉,帝尔激光公告,公司拟公开发行境外上市股份(H股)股票并申请在香港联交所主板挂牌上市,以深入推进国际化战略布局,打造多元化资本运作平台,提升国际品牌形象及全球市场综合竞争力。公司已通过第四届董事会第八次会议审议通过相关议案,尚需提交股东会审议。

腾讯推出OpenClaw安全工具箱

2026年3月12日 20:42
36氪获悉,3月12日,针对龙虾带来的安全挑战,腾讯推出了OpenClaw安全工具箱,为企业和用户提供安全保障。针对在云端部署的开发者与企业,腾讯云Lighthouse及ClawPro升级专属部署架构,支持环境隔离与快照回滚;腾讯云AI Agent安全中心可统一管理云上AI Agent,监测异常指令并扫描技能风险。对于将龙虾部署在个人电脑的用户,腾讯电脑管家18.0新增“龙虾管家-AI安全沙箱”功能,实现隔离运行。

宝马暂时放弃L3级自动驾驶

2026年3月12日 20:38
“宝马车型曾为车型配备L3级自动驾驶系统,但因该技术尚未达到商业化盈利水平,已暂时降低了L3级自动驾驶项目研发的优先级。”宝马集团全球CEO奥利弗·齐普策(Oliver Zipse)在2026财年会上表示,这并不代表宝马会放弃更高阶的自动驾驶技术研发,将持续关注自动驾驶技术发展趋势,一旦高阶自动驾驶技术出现可行的商业化模式,将立即推进相关研发工作。 (第一财经)

寒武纪:2025年净利润20.6亿元,上年同期亏损4.5亿元

2026年3月12日 20:35
36氪获悉,寒武纪公告,公司2025年实现营业收入64.97亿元,同比增长453.21%;归属于上市公司股东的净利润20.59亿元,同比扭亏为盈。公司拟向全体股东每10股派发现金红利15.00元(含税),以资本公积金向全体股东每10股转增4.9股。

广发证券:拟向广发香港增资不超过61.01亿港元

2026年3月12日 20:19
36氪获悉,广发证券公告,根据公司业务发展需要及广发香港的实际情况,公司拟向广发香港增资不超过61.01亿港元,公司根据实际情况可一次性增资或分批增资完成。公司本次向广发香港增资将进一步支持公司的国际业务发展,强化公司跨境服务能力,增厚广发香港资本实力,增强抗风险能力。

超讯通信:拟对外投资设立控股子公司,拓展在算力业务布局

2026年3月12日 20:17
36氪获悉,超讯通信公告,公司拟与广东微云科技股份有限公司(简称“微云科技”)共同投资设立超微智算(广东)科技有限公司(简称“超微智算”)。超微智算拟定注册资本为6000万元,其中公司拟以自有资金出资3060万元,持有超微智算51%股权。本次与合作方共同投资成立超微智算,旨在加快拓展公司在算力业务的布局,提升市场份额,符合公司增强转型的战略发展方向。

氪星晚报 |腾讯版小龙虾WorkBuddy发布全新版本欧睿国际:追觅扫地机全球高端销量第一;宝马集团2025年净利润同比下降3%

2026年3月12日 20:10

大公司:

中复神鹰:SYT80碳纤维新品的市场推广和应用评价等环节的周期可能较长

36氪获悉,中复神鹰发布异动公告,公司股票交易连续3个交易日内日收盘价格涨幅偏离值累计超过30%,属于股票交易异常波动情形。近日,多家媒体报道公司推出SYT80碳纤维新品,实现SYT80碳纤维的百吨级制备。本次发布的新产品虽已开始市场推广和应用评价,但该产品作为原材料要实现大规模销售,市场推广和应用评价等环节的周期可能较长,存在客户认证进度及未来销售价格波动等不确定性。

帝尔激光:2025年净利润5.19亿元,同比下降1.59%

36氪获悉,帝尔激光公告,公司2025年实现营业总收入20.33亿元,同比增长0.93%;归属于上市公司股东的净利润5.19亿元,同比下降1.59%;基本每股收益1.91元。

科大讯飞版“龙虾”AstronClaw上线

36氪获悉,3月12日,科大讯飞版“龙虾”——AstronClaw正式上线。其核心采用沙箱隔离技术运行,全程守护用户数据安全。支持云端一键部署,可接入企业微信、钉钉、飞书等主流通讯工具,直接对话即可指派任务。平台可调用10000+skills,支持自由切换星火X2、MiniMax-M2.5、Kimi-K2.5、GLM-5等多款大模型,胜任行业分析、文档处理等工作。

欧睿国际:追觅扫地机全球高端销量第一

3月12日,追觅扫地机获市场调查机构欧睿国际“全球高端扫地机器人销量第一”认证。目前,追觅扫地机遍布全球120个国家及地区,实现30国家市占率第一,在波兰、瑞典、丹麦、芬兰、德国等18个国家市占率超40%。

京东发布“智能机器人产业加速2.0计划”,助推品牌伙伴全年累计实现百亿营收

36氪获悉,3月12日,2026中国家电及消费电子博览会(AWE 2026)开幕,京东首次携手宇树科技、众擎、云深处、元点智能、镜识科技等多家机器人品牌共同亮相,集结超60款明星机器人产品。期间,京东正式发布“智能机器人产业加速2.0计划”,助力品牌伙伴在2026年内累计实现百亿规模营收。

鹏鼎控股:2025年净利润37.38亿元,同比增长3.25%

36氪获悉,鹏鼎控股公告,2025年公司实现营业收入391.47亿元,同比增长11.40%;归属于上市公司股东的净利润37.38亿元,同比增长3.25%。

有赞:2025年收入约为14.9亿元,同比增长约3%

36氪获悉,有赞发布2025年全年业绩报告。财报显示,2025年,有赞收入约为14.9亿元,同比增长约3%。其中订阅解决方案收入约为7.7亿元,商家解决方案收入约为7.1亿元,同比增长约9%。商家年度ARPU提升至28159元,同比增长约9%。毛利金额约为9.8亿元,毛利率约为66%。2025年,商家通过有赞的解决方案产生的GMV约为人民币1030亿元。其中,门店SaaS业务GMV达537亿元,在整体GMV中占比持续超过50%。

富途控股:2025年营收228.5亿港元,同比增长68.1%

36氪获悉,富途控股发布2025年第四季度及全年财报。财报显示,富途控股第四季度营收64.4亿港元,同比增长45.3%;Non-GAAP净利润34.6亿港元,同比增长77.0%。2025年全年营收228.5亿港元,同比增长68.1%;Non-GAAP净利润116.5亿港元,同比增长101.9%。

理想汽车:2025年第四季度营收288亿元,同比下降35%

36氪获悉,理想汽车发布2025年第四季度及全年财报。财报显示,理想汽车四季度营收288亿元,同比下降35%;净利润2020万元,上年同期净利润35亿元。2025年全年营收1123亿元,同比下降22.3%;净利润11亿元,同比下降85.8%。预计2026年第一季度车辆交付量为85000至90000辆,同比减少8.5%至3.1%;收入总额为204亿元至216亿元,同比减少21.3%至16.7%

张江高科:2025年净利润9.85亿元,同比增长0.30%

36氪获悉,张江高科公告,2025年公司实现营业收入41.89亿元,同比增长111.22%;归属于上市公司股东的净利润9.85亿元,同比增长0.30%;基本每股收益0.61元,同比下降3.17%。

阶跃星辰加入“小龙虾”战局,搭载开源社区最受欢迎“龙虾大脑”

3月12日,阶跃星辰推出基于OpenClaw打造的云端AI助手StepClaw,并开放5万个可一键部署并使用“小龙虾”的体验名额,限时免费1个月。体验名额不仅支持通过阶跃AI APP一键部署“小龙虾”,更提供了包含5000万模型Tokens、服务器、存储在内的全家桶免费权益。值得注意的是,StepClaw搭载了阶跃星辰Step 3.5 Flash。该模型自3月初全面开源后,登顶OpenClaw模型调用量月榜,并连续多日位列全球第一,是开源社区“最受开发者欢迎的龙虾大脑”。

本田宣布取消北美三款纯电车型研发

本田3月12日宣布,决定取消原计划在北美投产的三款电动汽车车型的开发与上市计划,此决定源于公司对汽车电气化战略的重新评估,该评估基于近期商业环境变化等多重因素。受此影响,本田预计2026财年(截至2026年3月)将出现合并财务亏损。(界面)

宝马集团2025年净利润同比下降3%

3月12日,宝马集团披露2025年业绩,全年共向全球客户交付约246.37万辆宝马、MINI和劳斯莱斯汽车,同比增长0.5%;集团总营收达1334.53亿欧元,同比下降6.3%;税前利润达102.36亿欧元,同比下降6.7%;净利润达74.51亿欧元,同比下降3.0%。(界面)

投融资:

“九有数据库”完成A+轮融资

36氪获悉,近日,深圳九有数据库有限公司宣布完成A+轮融资,引入松禾创投、青蒿资本、招银鼎洪投资三家投资机构。九有数据库创始人、CEO曾焱表示,未来公司将继续深耕国产超融合数据库领域,持续推进产品迭代与技术突破。

新产品:

晶核全球首发可量产60Ah全固态电池

36氪获悉,在AWE2026展会上,晶核能源CEO李延涛宣布全球首发60Ah超大全固态电池,将于2026年内正式向市场进行交付验证。同等体积下,电量接近传统电池的一倍,支持1-2C快充,并将电池装配效率提升至80%。此外,公司还为该电池打造了专属智能管理系统——晶核AI-BMS智能体系统,实现电池管理从“被动响应”向“主动预判”的升级。

腾讯版小龙虾WorkBuddy发布全新版本

36氪获悉,3月12日,腾讯云旗下AI原生桌面智能体工作台WorkBuddy(腾讯版小龙虾)正式发布全新版本,推出微信一键直连、企业微信长链接稳定接入、自动化任务执行与监控追踪、支持导入skills四大核心功能,大幅降低使用门槛,让普通用户也能轻松实现“养虾”办公。

今日观点:

自然资源部部长关志鸥:要加快推进不动产登记“跨省通办”

自然资源部部长关志鸥在十四届全国人大四次会议第三场“部长通道”上表示,近期要加快推进不动产登记“跨省通办”,实事求是解决林耕矛盾等历史遗留问题。(新华社)

其他值得关注的新闻:

澳大利亚临时放宽国内燃油质量标准以应对供应短缺

澳大利亚政府12日宣布,将在未来60天内临时调整该国燃油质量标准,允许硫含量更高的汽油进入市场,以增加国内燃油供应。澳大利亚气候变化与能源部长克里斯·鲍恩在当天发布的新闻公报中表示,此举将使原本可能出口的部分燃油转而供应国内市场,每月可新增约1亿升汽油供应。鲍恩说,这部分新增供应将优先投向面临燃油短缺的地区,以及为独立经销商和农业收割作业提供支持的批发现货市场。(新华社)

十四届全国人大四次会议批准2026年国民经济和社会发展计划

十四届全国人大四次会议表决通过了关于2025年国民经济和社会发展计划执行情况与2026年国民经济和社会发展计划的决议,批准2026年国民经济和社会发展计划。(新华社)

中信证券:子公司被香港证监会及廉署调查,公司及香港子公司经营情况正常

2026年3月12日 20:05
36氪获悉,中信证券公告,公司注意到近日部分媒体报道本公司一家香港子公司被香港证监会及廉政公署调查。经核实,2026年3月10日,香港证监会及廉署持搜查令到访香港子公司的营业地点,带走部分文件。香港子公司一名员工曾被廉署问话。公司对此高度重视,将继续密切关注此事进展情况。公司确认,截至公告披露日,公司及香港子公司经营情况正常,各项业务均保持正常开展。公司将根据上市地股票上市规则的规定履行信息披露义务。

中国人民银行:继续实施好适度宽松的货币政策,加大逆周期和跨周期调节力度

2026年3月12日 20:04
36氪获悉,据央行官网,3月12日,中国人民银行行长潘功胜主持召开经济金融专家座谈会。潘功胜表示,下一阶段,中国人民银行将全面贯彻落实党的二十届四中全会、中央经济工作会议精神和《政府工作报告》部署要求,构建科学稳健的货币政策体系,继续实施好适度宽松的货币政策,加大逆周期和跨周期调节力度,为经济持续向好向优创造适宜的货币金融环境。
❌
❌