news 2026/10/17 1:06:58

Android平台libX265交叉编译实战:从环境搭建到性能调优

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android平台libX265交叉编译实战:从环境搭建到性能调优

1. 为什么要在Android上折腾libX265

如果你正在做Android端的视频处理,尤其是涉及视频压缩、转码、推流或者本地录制,那大概率绕不开H.265(HEVC)这个编码格式。相比H.264,H.265在同等画质下能省下大约30%到50%的码率,这对移动端来说意义很大——存储空间更省、上传流量更少、弱网环境下的推流体验也更好。而libX265就是x265编码器的核心库,它是目前开源社区里H.265软编实现中最成熟、应用最广的一个。

但问题来了:Android平台默认并不提供libX265的预编译库。你要么用系统自带的MediaCodec硬编(受芯片和厂商影响大,参数控制粒度粗,画质调优空间小),要么自己动手把libX265交叉编译成Android能用的.so动态库。后者就是我这段时间实际在做的事情,踩了不少坑,也积累了一些经验,这里完整地梳理一遍。

这篇文章适合谁看?如果你是有一定C/C++基础、了解NDK基本用法、需要在Android项目里集成软编H.265能力的开发者,那这篇内容基本可以当作一份可复现的操作手册。如果你只是想知道H.265是什么、要不要用,那前面的背景部分也能帮你做判断。整篇内容围绕交叉编译这个核心动作展开,从工具链准备、源码配置、编译脚本编写,到产物验证和常见报错排查,尽量把每个环节的“为什么这么做”讲清楚。

先说结论:libX265的Android交叉编译本身并不算特别复杂,难点主要集中在CMake工具链文件的正确配置、汇编优化选项的取舍以及NDK版本与源码版本的兼容性这三块。把这三点搞定,剩下的就是耐心调参数。

2. 交叉编译的整体思路与方案选型

2.1 为什么选CMake而不是手写Makefile

libX265官方源码自带CMake构建系统,这是最省事的路径。早期x265用的是手写的Makefile加build/linux下的脚本,后来全面转向CMake,现在源码根目录下的CMakeLists.txt已经非常完善。用CMake的好处是,Android NDK从r19开始就内置了官方的CMake工具链文件android.toolchain.cmake,你不需要自己去拼--target、--sysroot这些参数,NDK帮你把交叉编译的复杂度封装掉了。

我试过两种方式:一种是直接用NDK的android.toolchain.cmake配合-DCMAKE_TOOLCHAIN_FILE参数;另一种是手动指定编译器路径。实测下来第一种稳定得多,尤其是涉及汇编优化的时候,工具链文件会自动处理-march、-mfpu这些架构相关的flag。手动指定很容易漏掉某个参数,导致编译出来的库在真机上跑不起来或者性能异常。

2.2 目标架构怎么选

Android目前主流是arm64-v8a,其次是armeabi-v7a,x86和x86_64主要用于模拟器。我的建议是至少编译arm64-v8a和armeabi-v7a两个版本,因为虽然现在新设备基本都是64位,但一些老设备和特定场景仍然需要32位库。x86系列如果你不做模拟器调试,可以暂时跳过,能省不少编译时间。

这里有个关键点:不同架构的汇编优化支持程度不一样。x265的汇编优化代码(在source/common/arm和source/common/x86目录下)对arm64的支持比较完善,对armeabi-v7a的NEON支持也还行,但32位ARM的汇编在某些NDK版本下会有兼容性问题。如果你编译armeabi-v7a时遇到汇编报错,可以先关掉汇编(-DENABLE_ASSEMBLY=OFF)验证一下是不是汇编的问题,确认后再针对性处理。

2.3 静态库还是动态库

libX265默认编译出来是静态库libx265.a,但Android项目里通常需要动态库.so。这里有两种做法:一是直接让CMake生成动态库(加-DBUILD_SHARED_LIBS=ON),二是先编静态库,再自己写一个JNI wrapper把它打包成.so。

我推荐第二种方式,原因有两个。第一,libX265的API是C接口,直接暴露给Java层需要JNI封装,你反正要写wrapper,不如把静态库链进去一起编。第二,静态库链接时可以只保留用到的符号,最终.so体积更可控。如果你直接编动态库,x265会把所有符号都导出,体积会大不少。当然,如果你只是想在native层用,不涉及Java调用,那直接编动态库也行。

2.4 NDK版本的选择

NDK版本这块我踩过坑。libX265的源码对NDK版本有一定要求,太老的NDK(比如r17之前)缺少一些C++17特性支持,太新的NDK(比如r26之后)又可能因为默认C++标准库变化导致链接问题。我实测下来r21到r25之间的版本比较稳,其中r23和r24是我用得最多的。

另外要注意NDK的STL选择。x265是C++写的,需要C++标准库。Android NDK提供了c++_static、c++_shared和system三种。如果你编静态库再自己封装,用c++_static比较省事,不用额外打包libc++_shared.so;如果你编动态库直接给别的模块用,那用c++_shared更合适,避免多个库各自带一份STL导致符号冲突。

3. 编译环境搭建与源码准备

3.1 基础工具清单

在开始之前,确认你的开发机上装了这些东西:

  • Android NDK:建议r23或r24,下载后解压到某个目录,记住路径,后面要用。
  • CMake:3.10以上版本,NDK自带的CMake也可以用,但独立安装一个更新版本更灵活。
  • Git:用来拉x265源码。
  • NASM(可选):如果你要编译x86架构且开启汇编,需要NASM。arm架构不需要。
  • Make或Ninja:构建工具,Ninja通常更快,推荐装一个。

Linux和macOS下这些工具都很好装,Windows下建议用WSL或者MSYS2,原生Windows环境编译x265会有路径和换行符的坑,不推荐。

3.2 拉取libX265源码

x265的官方源码托管在Bitbucket上,但国内访问不太稳定。我一般用镜像仓库或者直接下载release包。版本选择上,建议用3.5或3.6,这两个版本比较稳定,API也成熟。最新的4.x版本改动较大,如果你不是特别需要新特性,没必要追新。

# 假设你已经下载了x265_3.5.tar.gz tar -xzf x265_3.5.tar.gz cd x265_3.5

源码目录结构大致是这样的:

x265_3.5/ ├── source/ │ ├── common/ # 核心编码逻辑 │ ├── encoder/ # 编码器接口 │ ├── x265.h # 对外API头文件 │ └── ... ├── build/ # 各平台构建脚本 ├── CMakeLists.txt # 主构建文件 └── ...

你需要重点关注source/x265.h和source/x265_config.h,这两个是集成时要include的头文件。

3.3 编写CMake工具链配置

虽然NDK自带了android.toolchain.cmake,但直接用它有时候不够灵活。我习惯在项目根目录建一个android-toolchain.cmake,在里面设置一些通用参数,然后include NDK的工具链文件。

# android-toolchain.cmake set(ANDROID_NDK_PATH "/path/to/your/ndk" CACHE STRING "Android NDK path") set(CMAKE_SYSTEM_NAME Android) set(CMAKE_SYSTEM_VERSION 21) # 最低API级别 set(CMAKE_ANDROID_ARCH_ABI arm64-v8a) # 可通过外部覆盖 set(CMAKE_ANDROID_NDK ${ANDROID_NDK_PATH}) set(CMAKE_ANDROID_STL_TYPE c++_static) # 关闭一些不必要的特性,减小体积 set(ENABLE_CLI OFF CACHE BOOL "") set(ENABLE_SHARED OFF CACHE BOOL "") set(ENABLE_ASSEMBLY ON CACHE BOOL "") set(ENABLE_LIBNUMA OFF CACHE BOOL "")

这里解释几个关键参数:

  • CMAKE_SYSTEM_VERSION 21:对应Android 5.0,这是目前比较合理的最低支持版本。设太低(比如16)有些API用不了,设太高会限制设备覆盖范围。
  • CMAKE_ANDROID_STL_TYPE c++_static:静态链接STL,避免运行时依赖libc++_shared.so。
  • ENABLE_CLI OFF:不编译命令行工具,我们只要库。
  • ENABLE_ASSEMBLY ON:开启汇编优化,性能提升明显,但要注意兼容性。

3.4 一个容易忽略的点:x265_config.h的生成

x265的构建过程中会生成一个x265_config.h,里面定义了版本号、位深等宏。这个文件是CMake自动生成的,位置在构建目录下。如果你后续要集成到Android项目,需要把这个生成的头文件也拷贝过去,否则会报“找不到x265_config.h”的错误。

我一般会在编译完成后,把build/android/arm64-v8a/x265_config.h和源码里的source/x265.h一起放到一个include目录里,方便后续引用。

4. 编译脚本编写与实操过程

4.1 完整的编译脚本

下面是我实际用的编译脚本,以arm64-v8a为例,其他架构改一下ANDROID_ABI就行。

#!/bin/bash # build_android.sh NDK_PATH="/path/to/your/ndk" SOURCE_DIR="$(pwd)/x265_3.5" BUILD_DIR="$(pwd)/build/android" ABI="arm64-v8a" API_LEVEL=21 # 清理旧构建 rm -rf ${BUILD_DIR}/${ABI} mkdir -p ${BUILD_DIR}/${ABI} cd ${BUILD_DIR}/${ABI} cmake ${SOURCE_DIR}/source \ -DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake \ -DANDROID_ABI=${ABI} \ -DANDROID_PLATFORM=android-${API_LEVEL} \ -DCMAKE_BUILD_TYPE=Release \ -DANDROID_STL=c++_static \ -DENABLE_CLI=OFF \ -DENABLE_SHARED=OFF \ -DENABLE_ASSEMBLY=ON \ -DENABLE_LIBNUMA=OFF \ -DHIGH_BIT_DEPTH=OFF \ -DCMAKE_INSTALL_PREFIX=${BUILD_DIR}/${ABI}/install make -j$(nproc) make install

几个关键点说明:

  • cmake的源目录是${SOURCE_DIR}/source而不是${SOURCE_DIR},因为x265的CMakeLists.txt在source目录下。这一点很多人第一次会搞错。
  • -DANDROID_STL=c++_static和工具链文件里的设置要一致,否则可能冲突。
  • -DHIGH_BIT_DEPTH=OFF:关闭高位深,只编8bit。如果你需要10bit编码,改成ON,但要注意10bit和8bit的库不能混用,需要分别编译。
  • make -j$(nproc):并行编译,nproc是CPU核心数,能显著加快速度。

4.2 编译过程记录与耗时

以一台8核16线程的开发机为例,arm64-v8a的Release编译大概需要3到5分钟,armeabi-v7a稍快一些。如果开启汇编优化,编译时间会增加,因为汇编文件也要编译。我第一次编译时没开并行,花了将近20分钟,后来加上-j参数直接降到几分钟。

编译过程中你会看到大量类似这样的输出:

[ 12%] Building CXX object common/CMakeFiles/common.dir/arm/asm.S.o [ 15%] Building CXX object common/CMakeFiles/common.dir/arm/pixel-util.S.o ...

如果某个汇编文件报错,通常会明确提示是哪个文件、哪一行。这时候先别急着关汇编,看看是不是NDK版本的问题,换个NDK版本可能就好了。

4.3 产物验证

编译完成后,在install目录下应该能看到:

install/ ├── include/ │ ├── x265.h │ └── x265_config.h └── lib/ └── libx265.a

验证库的架构是否正确,用file命令:

file libx265.a # 应该输出类似:libx265.a: current ar archive

更准确的方式是用readelf看里面的目标文件:

ar x libx265.a file *.o | head -5 # 应该看到 ELF 64-bit LSB relocatable, ARM aarch64

如果是armeabi-v7a,应该看到ARM, EABI5。这一步很重要,我遇到过编译出来的库架构不对,结果在真机上加载失败的情况,排查了半天才发现是工具链配置写错了。

4.4 集成到Android项目的注意事项

把libx265.a和头文件集成到Android Studio项目时,有几点要注意:

第一,CMakeLists.txt里链接顺序。libX265依赖一些系统库,比如log、z(如果开启了zlib)、m(数学库)。链接顺序不对会报undefined reference。我一般这样写:

target_link_libraries( native-lib ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libx265.a log m z )

第二,头文件路径。把x265.h和x265_config.h放到cpp/include目录下,然后在CMake里include_directories。

第三,ABI过滤。在build.gradle里配置ndk { abiFilters 'arm64-v8a', 'armeabi-v7a' },确保只打包你编译了的架构。

第四,STL一致性。如果你编libX265用的是c++_static,那你的native模块也要用c++_static,否则可能出现STL符号冲突。这个坑我在一个项目里遇到过,表现为运行时崩溃,堆栈指向std::string的析构,查了很久才定位到STL不一致。

5. 常见问题与排查技巧实录

5.1 汇编相关报错

问题现象:编译arm64-v8a时,汇编文件报错,提示Error: unknown mnemonic或者invalid instruction。

排查思路:首先确认NDK版本,r21以下的NDK对某些ARMv8.2指令支持不好。其次检查-march参数,NDK工具链默认会根据ABI设置合适的-march,但如果你手动覆盖了,可能引入不支持的指令。

解决方法:升级NDK到r23以上,或者临时关闭汇编-DENABLE_ASSEMBLY=OFF验证。如果关掉汇编能编过,那就是汇编代码和工具链的兼容问题。长期方案是升级NDK,短期方案是关汇编(性能会下降约10%到20%)。

5.2 链接时undefined reference

问题现象:链接阶段报undefined reference to 'pthread_create'或者类似的系统函数。

排查思路:这是典型的链接库缺失。libX265用到了pthread、log等系统库,但CMake在交叉编译时不会自动链接。

解决方法:在你的wrapper的CMakeLists里显式加上log、m、z、atomic等库。如果是pthread相关,Android的pthread是集成在libc里的,一般不需要额外链接,但如果报错,可以试试加-pthread编译选项。

5.3 运行时崩溃:找不到符号

问题现象:APK安装后运行,加载native库时崩溃,logcat提示dlopen failed: cannot locate symbol。

排查思路:用readelf -d libyourlib.so看依赖的动态库,确认libc++_shared.so是否被打包。如果你用了c++_shared但没把libc++_shared.so放进jniLibs,就会报这个错。

解决方法:要么改用c++_static,要么在build.gradle里配置arguments "-DANDROID_STL=c++_shared"并确保NDK的libc++_shared.so被正确打包。我一般推荐前者,省事。

5.4 编码性能不达预期

问题现象:集成后编码速度很慢,1080p视频编码帧率只有个位数。

排查思路:先确认汇编是否开启。用nm libx265.a | grep asm看看有没有汇编符号。如果没有,说明汇编没编进去。其次检查编译类型,Debug版本会比Release慢很多。

解决方法:确保-DCMAKE_BUILD_TYPE=Release和-DENABLE_ASSEMBLY=ON。另外,x265的编码参数对性能影响很大,preset从medium调到veryfast能显著提速,但画质会下降。这个需要在业务层面权衡。

5.5 常见问题速查表

问题类型典型报错优先排查方向推荐解决方式
汇编编译失败unknown mnemonicNDK版本、-march参数升级NDK或关汇编
链接失败undefined reference缺失系统库补log/m/z/atomic
运行时崩溃cannot locate symbolSTL不一致统一用c++_static
性能差帧率低汇编未开、Debug构建开汇编、用Release
架构错误wrong ELF classABI配置错误检查工具链ABI设置
头文件缺失x265_config.h not found未拷贝生成的头文件从build目录拷贝

5.6 几个独家避坑技巧

技巧一:先编一个最小Demo验证。不要一上来就集成到主项目,先写一个最简单的JNI函数,调用x265_api_get打印版本号,确认库能正常加载和调用。这一步能帮你快速排除环境问题。

技巧二:保留编译日志。每次编译把输出重定向到文件,make -j8 2>&1 | tee build.log。出问题时可以回溯,尤其是并行编译时错误信息会交错,日志文件能帮你理清顺序。

技巧三:版本锁定。NDK版本、x265版本、CMake版本都固定下来,写进项目文档。我吃过亏,换了个NDK版本后编译出来的库行为不一致,排查了很久。团队协作时尤其要注意这点。

技巧四:10bit和8bit分开管理。如果你两个都要,建议编成两个不同的库名,比如libx265_8bit.a和libx265_10bit.a,避免混淆。x265的API里通过x265_api_get(bitDepth)来获取对应位深的API,链接时要注意别搞混。

技巧五:注意API级别和NDK的对应关系。ANDROID_PLATFORM设成android-21意味着你的库只能在API 21以上运行。如果你的App最低支持API 19,那要么把库的API级别降到19,要么在代码里做版本判断。不过现在API 21以下的设备已经很少了,一般设21没问题。

6. 进阶:多架构批量编译与自动化

6.1 批量编译脚本

手动一个个架构编译太累,写个循环脚本一次搞定:

#!/bin/bash NDK_PATH="/path/to/your/ndk" SOURCE_DIR="$(pwd)/x265_3.5" BUILD_ROOT="$(pwd)/build/android" API_LEVEL=21 ABIS=("arm64-v8a" "armeabi-v7a") for ABI in "${ABIS[@]}"; do echo "========== Building for ${ABI} ==========" BUILD_DIR="${BUILD_ROOT}/${ABI}" rm -rf ${BUILD_DIR} mkdir -p ${BUILD_DIR} cd ${BUILD_DIR} cmake ${SOURCE_DIR}/source \ -DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake \ -DANDROID_ABI=${ABI} \ -DANDROID_PLATFORM=android-${API_LEVEL} \ -DCMAKE_BUILD_TYPE=Release \ -DANDROID_STL=c++_static \ -DENABLE_CLI=OFF \ -DENABLE_SHARED=OFF \ -DENABLE_ASSEMBLY=ON \ -DENABLE_LIBNUMA=OFF \ -DHIGH_BIT_DEPTH=OFF \ -DCMAKE_INSTALL_PREFIX=${BUILD_DIR}/install make -j$(nproc) 2>&1 | tee build.log make install done echo "All builds completed."

这个脚本会依次编译arm64-v8a和armeabi-v7a,每个架构的日志单独保存。如果你想加x86_64,往ABIS数组里加就行。

6.2 用Ninja加速构建

CMake默认用Make,换成Ninja能快一些,尤其是增量编译的时候。加一个参数就行:

cmake ... -G Ninja ninja -j$(nproc)

Ninja的并行调度比Make更高效,大项目上差距明显。不过x265的规模不算特别大,提升有限,但聊胜于无。

6.3 产物整理与版本管理

编译完成后,建议把产物按架构整理好,方便集成:

prebuilt/ ├── arm64-v8a/ │ ├── include/ │ │ ├── x265.h │ │ └── x265_config.h │ └── lib/ │ └── libx265.a └── armeabi-v7a/ ├── include/ └── lib/

然后把这个prebuilt目录纳入版本管理(或者用Git LFS),这样团队成员不用每个人都编译一遍。记得在README里写清楚编译用的NDK版本和x265版本,方便追溯。

6.4 关于10bit支持的补充

如果你的业务需要10bit编码(比如HDR视频),编译时要加-DHIGH_BIT_DEPTH=ON。但要注意,10bit和8bit的库不能同时链接到一个模块里,因为符号名会冲突。x265的做法是通过x265_api_get在运行时选择,但静态库链接时还是需要分开。

我的做法是编两个独立的静态库,然后在wrapper里根据需求链接对应的那个。如果两个都要支持,那就编两个wrapper模块,或者用动态加载的方式在运行时决定加载哪个。后者复杂度高一些,但灵活性最好。

6.5 体积优化

libX265编出来的静态库不小,arm64-v8a的Release版本大概在3到5MB。如果你对APK体积敏感,可以做几件事:

第一,关闭不需要的特性。比如-DENABLE_LIBNUMA=OFF、-DENABLE_PPAFF=OFF(如果不用PAFF)、-DENABLE_VTZ=OFF等。具体哪些能关,看你的业务需求。

第二,用strip去掉符号表。静态库在链接进最终.so后,可以用llvm-strip去掉调试符号,能省不少体积。

第三,只保留用到的编码功能。x265支持很多编码工具,如果你只用基础功能,可以在编译时通过宏定义裁剪。但这个改动比较大,需要改源码,一般不建议。

第四,考虑用-Os而不是-O3。-Os优化体积,-O3优化速度。移动端一般更看重速度,所以我还是推荐-O3,体积问题通过其他方式解决。

7. 实际集成中的性能调优经验

7.1 编码参数对性能的影响

libX265的编码速度受preset影响极大。我实测过同一段1080p视频在不同preset下的表现:

Preset编码速度(fps)相对质量适用场景
ultrafast45较低实时推流
veryfast28中等快速转码
medium12较高本地存储
slow5高离线处理

移动端一般用veryfast或medium比较平衡。ultrafast虽然快,但码率会明显上升,省流量的初衷就打折扣了。

7.2 线程数配置

x265支持多线程编码,通过x265_param里的frameNumThreads控制。移动端CPU核心数有限,一般设成2到4比较合适。设太多会导致线程切换开销增大,反而变慢。我一般设成min(4, cpu核心数)。

另外,poolNumThreads控制线程池大小,和frameNumThreads配合使用。默认值通常够用,不需要特别调。

7.3 内存占用控制

软编H.265的内存占用不低,1080p编码大概需要100到200MB。如果你的App对内存敏感,可以通过降低frameNumThreads、减小rcLookahead来降低内存。但代价是编码效率下降。这个需要根据设备档次做动态调整,高端机用高配置,低端机降配置。

7.4 与硬编的取舍

最后说一句硬编和软编的选择。硬编(MediaCodec)速度快、功耗低,但画质和参数控制不如软编。如果你的场景是实时推流、对延迟敏感,硬编更合适。如果是本地视频压缩、追求画质和压缩率,软编(libX265)更好。实际项目中,很多方案是两者结合:预览和实时通信用硬编,最终存储用软编。

我在一个项目里就是这么做的,录制时用硬编保证流畅,录制完成后在后台用libX265重新编码一遍,画质提升明显,文件体积还小了不少。这个思路供你参考。

内容到这里基本覆盖了libX265在Android上交叉编译的完整流程。从环境准备到脚本编写,从问题排查到性能调优,每一步都是我实际踩过坑之后总结出来的。如果你在操作过程中遇到这里没提到的问题,大概率是NDK版本或者源码版本的差异导致的,换个版本试试往往能解决。编译这种事,有时候就是玄学,多试几次总能过。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 1:06:42

HI3556V200双系统核间通信:IPCM/virt_tty/sharefs选型与实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/17 1:06:24

双节锂电池主动均衡设计:MP2642GR-Z与STM32F732IE协同方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/17 1:05:42

基于MP2642GR-Z与STM32F412RE的2串电池被动均衡方案设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/17 1:05:27

K-Means聚类算法C++实现:从原理到工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/17 1:05:16

0/1背包问题详解:动态规划、状态转移方程与滚动数组优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/17 1:04:39

双串锂电池主动均衡方案:MP2642GR-Z与PIC18LF46K42协同设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华