1594. 矩阵的最大非负积

1594. 矩阵的最大非负积 给你一个大小为 m x n 的矩阵 grid 。最初,你位于左上角 (0, 0) ,每一步,你可以在矩阵中 向右 或 向下 移动。 在从左上角 (0, 0) 开始到右下角 (m - 1, n - 1) 结束的所有路径中,找出具有 最大非负积 的路径。路径的积是沿路径访问的单元格中所有整数的乘积。 返回 最大非负积 对 $10^9 + 7$ 取余 的结果。如果最大积为 负数 ,则返回 -1 。 注意取余是在得到最大积之后执行的。 提示: m == grid.length n == grid[i].length 1 <= m, n <= 15 -4 <= grid[i][j] <= 4 思路 首先,这是一个典型的dp问题,要求从左上角到右下角的路径乘积和最大非负积 取余 的结果。 可以使用两个和gird大小一致的数组big,sml分别存放到某个位置(i,j)的最大和最小乘积。 计算时注意,最上一行没有来自上的计算,最左一行没有来自左的计算,所以计算时先初始化(0,0)位置的big[0][0]=sml[0][0]=grid[0][0],然后计算最上一行、最左一行,最后计算剩下的。 点击展开/折叠 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Solution { public: int maxProductPath(vector<vector<int>>& grid) { const int MOD = 1e9 + 7; int m = grid.size(), n = grid[0].size(); vector<vector<long long>> big(m, vector<long long>(n)); vector<vector<long long>> sml(m, vector<long long>(n)); big[0][0] = grid[0][0]; sml[0][0] = grid[0][0]; for (int i = 1; i < m; ++i) { big[i][0] = sml[i][0] = (long long)big[i-1][0] * grid[i][0]; } for (int j = 1; j < n; ++j) { big[0][j] = sml[0][j] = (long long)big[0][j-1] * grid[0][j]; } for (int i = 1; i < m; ++i) { for (int j = 1; j < n; ++j) { vector<long long> candidates = { (long long)big[i-1][j] * grid[i][j], (long long)sml[i-1][j] * grid[i][j], (long long)big[i][j-1] * grid[i][j], (long long)sml[i][j-1] * grid[i][j] }; big[i][j] = *max_element(candidates.begin(), candidates.end()); sml[i][j] = *min_element(candidates.begin(), candidates.end()); } } return big[m-1][n-1] >= 0 ? (big[m-1][n-1] % MOD) : -1; } };

March 8, 2026 · 2 min · farmer3-c

1888. 使二进制字符串字符交替的最少反转次数

1888. 使二进制字符串字符交替的最少反转次数 给你一个二进制字符串 s 。你可以按任意顺序执行以下两种操作任意次: 类型 1 :删除 字符串 s 的第一个字符并将它 添加 到字符串结尾。 类型 2 :选择 字符串 s 中任意一个字符并将该字符 反转 ,也就是如果值为 '0' ,则反转得到 '1' ,反之亦然。 请你返回使 s 变成 交替 字符串的前提下, 类型 2 的 最少 操作次数 。 我们称一个字符串是 交替 的,需要满足任意相邻字符都不同。 比方说,字符串 "010" 和 "1010" 都是交替的,但是字符串 "0100" 不是。 提示: $1 <= s.length <= 10^5$ s[i] 要么是 '0' ,要么是 '1' 。 思路 首先,目的是得到0101..或1010..样式的字符串。 类型1可以操作任意次,也就是说可以使用s+s的长度为 n 的子串和目标字符串进行比较。 使用cnt记录达到0101..需要进行的类型2操作数,n-cnt 就是达到0101..需要进行的类型2操作数。 每次向右滑动一位,出滑动窗口的字符与目标字符串对应位进行比较,若不同,则cnt–,因为之前记录时加入了修改用的操作数;进滑动窗口的字符与目标字符串对应位进行比较,若不同,同理,cnt++。 每滑一次,更新ans=min({ans,n-cnt,cnt})。 点击展开/折叠 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution { public: int minFlips(string s) { int n = s.size(); string tar = "01"; int cnt = 0; for (int i = 0; i < n; i++) { cnt += (s[i] != tar[i % 2]); } int ans = min({cnt, n - cnt}); for (int i = 0; i < n; i++) { cnt -= (s[i] != tar[i % 2]); cnt += (s[i] != tar[(i + n) % 2]); ans = min({ans, cnt, n - cnt}); } return ans; } };

March 7, 2026 · 1 min · farmer3-c

2266. 统计打字方案数

2266. 统计打字方案数 Alice 在给 Bob 用手机打字。数字到字母的 对应 如下图所示。 为了 打出 一个字母,Alice 需要 按 对应字母 i 次,i 是该字母在这个按键上所处的位置。 比方说,为了按出字母 's' ,Alice 需要按 '7' 四次。类似的, Alice 需要按 '5' 两次得到字母 'k' 。 注意,数字 '0' 和 '1' 不映射到任何字母,所以 Alice 不 使用它们。 但是,由于传输的错误,Bob 没有收到 Alice 打字的字母信息,反而收到了 按键的字符串信息 。 比方说,Alice 发出的信息为 "bob" ,Bob 将收到字符串 "2266622" 。 给你一个字符串 pressedKeys ,表示 Bob 收到的字符串,请你返回 Alice 总共可能发出多少种文字信息 。 由于答案可能很大,将它对 $10^9 + 7$ 取余 后返回。 提示: $1 <= pressedKeys.length <= 10^5$ pressedKeys 只包含数字 '2' 到 '9' 。 题解 首先,这是一个动态规划问题,接收的字符串 pressedKeys 可以被分割成多个子问题,每个子问题都是一个子字符串,子字符串为同样字符。 ...

March 6, 2026 · 2 min · farmer3-c

博客网站 自动更新

问题 1:如何让网站自动更新,无需手动刷新 问题描述: 添加新文章或变化后,需要手动刷新才能看到更新 解决方案: 添加了 Jekyll Livereload 支持( npm run start:live ) 配置了 Service Worker 自动更新检测 添加了 BrowserSync 作为可选方案 修改文件: package.json - 添加新脚本命令 Gruntfile.js - 配置 BrowserSync 问题 2:还是会有刷新确认按钮 问题描述: 网站检测到更新时,会显示 “Content updated. REFRESH” 按钮,需要手动点击 解决方案: 修改 sw-registration.js ,移除手动刷新提示,改为直接自动刷新 修改文件: js/sw-registration.js - 移除 createSnackbar 调用,直接使用 location.reload() 问题 3:网站不停自动更新(无限循环) 问题描述: 打开网站后不停自动刷新 解决方案: 移除了自动刷新逻辑 改用 Stale-While-Revalidate 策略:先显示缓存,后台更新 用户下次访问时自然看到新内容 修改文件: sw.js - 移除 UPDATE_FOUND 消息发送 js/sw-registration.js - 移除自动刷新 问题 4:删除的文章仍在网站上显示 问题描述: 从仓库删除的文章,网站上还能看到 解决方案: ...

February 16, 2026 · 2 min · farmer3-c

编译实践Lv0. 环境配置

目的 配置实验环境的 Docker 容器, 并学习 Docker 的基本使用方法. 认识编译实践中用到的编译器中间表示: Koopa IR. 认识编译实践中开发的编译器的目标架构: RISC-V. 配置实验环境的 Docker 容器, 并学习 Docker 的基本使用方法. 安装Docker Desktop 使用docker 一、镜像相关常用命令 镜像是容器的 “模板”,所有容器都基于镜像创建,这部分是基础操作。 1. 拉取镜像(下载到本地) 如果本地没有该镜像,先从 Docker 仓库拉取(如果是私有仓库,需先docker login): powershell 1 2 3 4 5 # 拉取指定镜像(格式:docker pull 镜像名:标签,latest是默认标签可省略) docker pull maxxing/compiler-dev:latest # 简写(省略latest,效果同上) docker pull maxxing/compiler-dev 作用:把远程仓库的maxxing/compiler-dev镜像下载到本地,后续创建容器无需重复下载; 场景:首次使用该镜像、需要更新镜像版本时。 2. 查看本地镜像 powershell 1 2 3 4 5 # 查看所有本地镜像(重点看REPOSITORY、TAG、IMAGE ID) docker images # 过滤查看指定镜像(只显示maxxing/compiler-dev) docker images maxxing/compiler-dev 输出示例: 1 2 REPOSITORY TAG IMAGE ID CREATED SIZE maxxing/compiler-dev latest 5bcecbbe34b6 3 months ago 4.14GB 场景:确认镜像是否已下载、查看镜像 ID / 大小等信息。 ...

February 14, 2026 · 6 min · farmer3-c

三段式数组 II

三段式数组 II 给你一个长度为 n 的整数数组 nums。 三段式子数组 是一个连续子数组 nums[l…r](满足 0 <= l < r < n),并且存在下标 l < p < q < r,使得: nums[l…p] 严格 递增, nums[p…q] 严格 递减, nums[q…r] 严格 递增。 请你从数组 nums 的所有三段式子数组中找出和最大的那个,并返回其 最大 和。 提示: $4 <= n = nums.length <= 10^5$ $-10^9 <= nums[i] <= 10^9$ 保证至少存在一个三段式子数组。 思路 由于l < p < q < r, $p$ 是第一段的终点和第二段的起点,$q$ 是第二段的终点和第三段的起点,我们可以通过预处理和线性扫描来解决。 preSum[i]: 以 $i$ 结尾且满足“严格递增”的最大子数组和。 条件:必须包含 $nums[i]$ 和 $nums[i-1]$。 ...

February 4, 2026 · 2 min · farmer3-c

System.IO.FileLoadException: 未能加载文件或程序集“WindowsDebugLauncher……

问题 在使用VS code编写C++程序时,运行程序时出现了以下错误: 未经处理的异常: System.IO.FileLoadException: 未能加载文件或程序集“WindowsDebugLauncher, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”或它的某一个依赖项。强名称验证失败。 (异常来自 HRESULT:0x8013141A) ---> System.Security.SecurityException: 强名称验证失败。 (异常来自 HRESULT:0x8013141A) --- 内部异常堆栈跟踪的结尾 --- 解决方法 异常信息的重点在:强名称验证失败。 这个问题主要出现在c#开发,在c#中,程序集的强名称是由程序集的公钥和版本号组成的。如果程序集的强名称验证失败,就会出现这个错误。 所以我想先尝试临时禁用强名称验证(仅用于调试环境),以查看是否能解决问题。 因为没有安装 sn.exe 工具,所以通过安装 .NET SDK 来获取 sn.exe 工具。 安装好后,我灵机一动,是不是可以删除VS code “PORTS”面板的进程,然后再重新运行程序,看看是否能解决问题。 于是我在“PORTS”面板中 结束了所有进程 ,然后再重新打开VS code,运行程序,问题解决了。 猜测原因 首先要明确:这个错误表面是 C++ 运行报错,实际是 VS Code 的 C/C++ 调试插件依赖的.NET 程序集出了问题,不是 C++ 代码本身有逻辑错误。 WindowsDebugLauncher是 VS Code 的 C/C++ 调试插件(ms-vscode.cpptools)在 Windows 平台上依赖的调试启动程序集(基于.NET Framework 开发),它带有强名称签名(公钥令牌正是b03f5f7f11d50a3a)。强名称验证失败的核心是:系统在加载这个程序集时,验证它的签名完整性、版本一致性、公钥合法性时出了问题,导致拒绝加载,最终调试器无法启动,抛出FileLoadException。 调试进程残留 之前多次启动 C++ 程序调试,部分调试进程(hello.exe、use4set.exe)没有正常退出,而是在后台残留,对应的cppdbg调试器也没有释放。 这些残留进程会占用WindowsDebugLauncher程序集的资源,或者导致程序集被锁定在 “已加载但未释放” 的状态,后续再次启动调试时,系统无法重新验证该程序集的强名称(相当于 “资源被占用,验证流程无法正常执行”),从而抛出验证失败错误。 ...

February 3, 2026 · 1 min · farmer3-c

c++怎么用set

什么是set 简单来说,set是一个集合,里面的元素是唯一的,而且是有序的。 set是一个容器,可以提供快速的查找、插入和删除操作,具有对数复杂度。 set 通常实现为 红黑树。 怎么用set 迭代器 begin 返回指向起始的迭代器 cbegin (C++11) (public 成员函数) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <algorithm> #include <iostream> #include <set> int main() { std::set<int> set{3, 1, 4, 1, 5, 9, 2, 6, 5}; // std::for_each(set.begin(), set.end(), [](int x) // { // std::cout << x << ' '; // }); std::cout<<*set.begin(); std::cout << '\n'; } end 返回指向末尾的迭代器 cend (C++11) (public 成员函数) cbegin()和cend()是C++11新增的,它们返回一个const的迭代器,不能用于修改元素。 ...

February 2, 2026 · 14 min · farmer3-c

将数组分成最小总代价的子数组 II

将数组分成最小总代价的子数组 II 给你一个下标从 0 开始长度为 n 的整数数组 nums 和两个 正 整数 k 和 dist 。 一个数组的 代价 是数组中的 第一个 元素。比方说,[1,2,3] 的代价为 1 ,[3,4,1] 的代价为 3 。 你需要将 nums 分割成 k 个 连续且互不相交 的子数组,满足 第二 个子数组与第 k 个子数组中第一个元素的下标距离 不超过 dist 。换句话说,如果你将 nums 分割成子数组 $nums[0..(i_1 - 1)], nums[i_1..(i_2 - 1)], ..., nums[i_{k-1}..(n - 1)] ,$ 那么它需要满足 $i_{k-1} - i_1 <= dist$ 请你返回这些子数组的 最小 总代价。 提示: 3 <= n <= 105 1 <= nums[i] <= 109 3 <= k <= n k - 2 <= dist <= n - 2 思路 从将 nums 分割成 k 个 连续且互不相交 的子数组得到 ...

February 2, 2026 · 1 min · farmer3-c

开源软件导论

第一章:开源概述 开源是什么 在 20 世纪 70 年代末,Richard Stallman 在 MIT 人工智能实验室安装了一台新的打印机,当时他在那里工作,这台打印机运行专有固件。打印机出现问题时,理查德·斯托曼很沮丧,因为他无法获得打印机软件的副本并编辑代码来解决。这种挫败感时自由与开源运动的起源。 对于所有人而言,开源的好处: 获取任何给定工作的源代码、配置文件和文档。 自由地使用、修改和分发这些代码。 摆脱厂商的恶意绑定 更高的合作程度 对于开源的使用者而言,他们想要达成目标不会受到制约,最大程度地发挥自己的意愿。 对于开源的贡献者而言,他们可以在不付出实际代价的情况下,为开源软件做出贡献,在自己的简历中展示自己的能力。 开源历史上的 6 个关键时刻 RMS 和打印机 在 20 世纪 70 年代末,Richard·M·Stallman(RMS)是麻省理工学院的一名员工程序员。他的部门像当时许多大学一样,共享一台 PDP-10 计算机和一台打印机。他们遇到的一个问题是,纸经常在打印机中卡住,导致一连串的打印任务堆积在队列中,直到有人修理好卡纸问题。为了解决这个问题,麻省理工学院的员工想出了一个很棒的社会性解决方案:他们编写了打印机驱动程序的代码,以便当打印机卡住时,会向所有当前等待打印任务的人发送一条消息:“打印机卡纸了,请修理。“这样,它就不会长时间卡住。 1980 年,实验室接受了一台全新的激光打印机的捐赠。然而,当斯托曼要求获取打印机驱动程序的源代码,以便重新实现社会黑客技术,让系统在纸张卡住时通知用户时,他被告知这是专有信息。他听说另一所大学的一位研究员拥有一个研究项目的源代码,当机会出现时,他请求这位同事分享——而对方拒绝时他感到震惊。他们签署了保密协议(NDA),斯托曼认为这是对黑客文化的背叛。 20 世纪 70 年代末至 80 年代初,软件被视为有价值,这种软件传统上与硬件一起以源代码形式提供。越来越多的麻省理工学院研究人员开始创办软件公司,而销售软件许可证是他们商业模式的关键。保密协议和专有软件许可证成为常态,最好的程序员被从麻省理工学院等大学招聘来参与私人开发项目,在那里他们无法再分享或合作。 作为对此的反应,斯托曼决定创建一个完整的操作系统,它不会剥夺用户理解其工作原理的自由,并且如果他们愿意,还可以进行修改。这就是自由软件运动的诞生。 GNU 的创建和自由软件的兴起 到 1983 年底,斯托曼准备好宣布他的项目并招募支持者和助手。1983 年 9 月,他宣布了 GNU 项目的创建(GNU 代表 Gnu’s Not Unix——一个递归缩写)。该项目的目标是克隆 Unix 操作系统,以创建一个赋予用户完全自由的系统。 1984 年 1 月,他开始全职从事该项目,首先创建了一个编译器系统(GCC)和各种操作系统工具。1985 年初,他发布了《GNU 宣言》,号召程序员加入,并成立了自由软件基金会以接受捐赠来支持这项工作。这份文件是自由软件运动的创始章程。 《GNU 通用公共许可证》的编写 直到 1989 年,自由软件基金会和理查德·斯托曼(RMS)编写并发布的软件都没有统一的许可证。Emacs 在 Emacs 许可证下发布,GCC 在 GCC 许可证下发布,等等;然而,在一家名为 Unipress 的公司强迫斯托曼停止分发他们从詹姆斯·高斯林(Java 之父)那里获得的 Emacs 实现版本后,他意识到需要一种保护用户自由的许可证。 ...

January 28, 2026 · 3 min · farmer3-c