2026-01-29:统计镜子反射路径数目。用go语言,给定一个大小为 m × n 的二值网格 grid(0 表示空格,1 表示镜子)。机器人从左上角 (0,0) 出发,目标是到达右下角 (m−1,n

2026-01-29:统计镜子反射路径数目。用go语言,给定一个大小为 m × n 的二值网格 grid(0 表示空格,1 表示镜子)。机器人从左上角 (0,0) 出发,目标是到达右下角 (m−1,n−1)。机器人每步只能向右或向下移动,但如果准备进入的格子里有镜子,它不会直接进入,而是在进入前被“反射”改换方向并跳到镜子相应一格之外的位置:

  • 若机器人想向右进入一个镜子格子,它会被转向向下并移动到该镜子的正下方格子;
  • 若机器人想向下进入一个镜子格子,它会被转向向右并移动到该镜子的正右方格子。

如果这样的反射使机器人移出网格,则该路径无效,不计入答案。注意:若反射后到达的格子仍然是镜子,会立即按照进入时的方向再发生一次反射(反射方向由当次进入的移动方向决定)。求从起点到终点的所有不同有效路径数,并对 1000000007 取模返回结果。

m == grid.length。

n == grid[i].length。

2 <= m, n <= 500。

grid[i][j] 的值为 0 或 1。

grid[0][0] == grid[m - 1][n - 1] == 0。

输入: grid = [[0,1,0],[0,0,1],[1,0,0]]。

输出: 5。

解释:

编号完整路径
1(0, 0) → (0, 1) [M] → (1, 1) → (1, 2) [M] → (2, 2)
2(0, 0) → (0, 1) [M] → (1, 1) → (2, 1) → (2, 2)
3(0, 0) → (1, 0) → (1, 1) → (1, 2) [M] → (2, 2)
4(0, 0) → (1, 0) → (1, 1) → (2, 1) → (2, 2)
5(0, 0) → (1, 0) → (2, 0) [M] → (2, 1) → (2, 2)

[M] 表示机器人试图进入一个有镜子的格子但被反射了。

题目来自力扣3665。

算法过程描述

该算法采用动态规划,逐行处理网格,并维护一个状态数组来记录到达当前行各列的路径数。其核心在于通过两个状态来区分机器人进入某个格子时的移动方向。

  1. 初始化
    • 定义模数 mod = 1_000_000_007 用于结果取模。
    • 获取网格的列数 n
    • 创建状态数组 f,其大小为 n+1(索引从0到n)。数组的每个元素是一个包含两个整数的数组 [2]int
      • f[j][0] 表示从上方格子移动到当前行第 j 列(即从 (i-1, j) 向下移动)的累计路径数。
      • f[j][1] 表示从左侧格子移动到当前行第 j 列(即从 (i, j-1) 向右移动)的累计路径数。
    • 初始化起点 (0,0) 的状态。由于机器人起点在 (0,0),并且只能向右或向下移动,因此可以理解为存在一条“从上方来”的路径和一条“从左方来”的路径到达 (0,0)(尽管逻辑上起点没有前驱)。代码通过将 f[1](对应第0列)初始化为 {1, 1} 来巧妙地设定初始状态。
  2. 遍历网格
    • 外层循环遍历网格的每一行 row
    • 内层循环遍历当前行的每一个格子 (i, j),其值为 x(0或1)。
  3. 状态转移(核心逻辑)
    对于每个格子 (i, j),根据其是否为镜子(x 的值)更新状态 f[j+1](因为 f 的索引从1开始,对应网格列索引0):
    • 情况一:当前格子是空格 (x == 0)
      • f[j+1][0] 更新(来自上方的路径):如果机器人从上方 (i-1, j) 向下移动进入这个空格,它可以继续向下一个格子移动。因此,到达此格子的路径数等于从上方到达当前格子的路径数 f[j][0] 加上从左侧到达当前格子的路径数 f[j+1][1](因为从左侧进入空格后,方向不变,可以继续向右或向下,但这里 f[j+1][1] 已经包含了从左侧到达 (i, j) 的路径信息,用于更新当前状态)。代码中的逻辑是 f[j+1][0] = (f[j][0] + f[j+1][1]) % mod
      • f[j+1][1] 更新(来自左侧的路径):如果机器人从左侧 (i, j-1) 向右移动进入这个空格,其路径数与从上方进入的情况相同,因为空格不改变方向。所以 f[j+1][1] 被设置为与 f[j+1][0] 相同的值。
      • 简单来说,对于空格,无论从哪个方向进入,都能正常通过,因此到达该格子的路径数合并了来自上方和左侧的路径。
    • 情况二:当前格子是镜子 (x == 1)镜子格子的状态转移是算法最巧妙的部分,它通过交换和叠加状态来模拟反射行为,避免了显式地追踪反射跳转。
      • f[j+1][0] 更新(来自上方的路径):如果机器人从上方 (i-1, j) 向下移动试图进入这个镜子格子,它会被反射。根据规则,向下进入镜子会被转向右,并移动到镜子正右方的格子 (i, j+1)。在动态规划的状态更新中,这个反射动作意味着:原本计划到达镜子格子 (i, j) 的路径(由 f[j][0] 表示),现在实际上被转移到了其右侧的格子 (i, j+1) 的“来自左侧的路径”上。因此,代码中 f[j+1][0] 的值被更新为 f[j+1][1],可以理解为将上方来的路径“叠加”到右侧格子来自左侧的路径上。
      • f[j+1][1] 更新(来自左侧的路径):如果机器人从左侧 (i, j-1) 向右移动试图进入这个镜子格子,它会被反射。根据规则,向右进入镜子会被转向下,并移动到镜子正下方的格子 (i+1, j)。在状态更新中,这个反射动作意味着:原本计划到达镜子格子 (i, j) 的路径(由 f[j+1][1] 表示),现在实际上被转移到了其下方格子。由于算法是逐行处理的,在计算当前行时,下方格子的状态尚未更新。因此,这里的更新 f[j+1][1] = f[j][0] 可以理解为一种状态传递或记录,确保在后续处理中能正确计算反射效果。更准确地说,它记录了由于反射,从左侧来的路径如何影响下方格子的状态。
  4. 返回结果
    • 当所有行和列都处理完毕后,状态数组 f 的最后一个元素 f[n][0] 存储的就是从起点 (0,0) 到达终点 (m-1, n-1) 的所有不同有效路径数,并对 mod 取模后的结果。这里取 f[n][0] 是因为到达终点通常可以理解为是从上方(即终点所在行的上一行)移动下来的最后一步。

复杂度分析

  • 时间复杂度:算法需要遍历网格中的每个格子一次,总共遍历 m * n 个格子。每个格子的处理都是常数时间的操作。因此,总的时间复杂度为 O(m * n)
  • 空间复杂度:算法使用了一个大小为 n+1 的状态数组 f,其中每个元素是两个整数。这个数组的大小只与列数 n 有关,与行数 m 无关。因此,总的额外空间复杂度为 O(n)

Go完整代码如下:

package main import("fmt")funcuniquePaths(grid [][]int)(ans int){const mod =1_000_000_007 n :=len(grid[0]) f :=make([][2]int, n+1) f[1]=[2]int{1,1}for_, row :=range grid {for j, x :=range row {if x ==0{ f[j+1][0]=(f[j][0]+ f[j+1][1])% mod f[j+1][1]= f[j+1][0]}else{ f[j+1][0]= f[j+1][1] f[j+1][1]= f[j][0]}}}return f[n][0]}funcmain(){ grid :=[][]int{{0,1,0},{0,0,1},{1,0,0}} result :=uniquePaths(grid) fmt.Println(result)}
在这里插入图片描述

Python完整代码如下:

# -*-coding:utf-8-*-defuniquePaths(grid): MOD =1_000_000_007 n =len(grid[0]) f =[[0,0]for _ inrange(n +1)] f[1]=[1,1]for row in grid:for j, x inenumerate(row):if x ==0: f[j +1][0]=(f[j][0]+ f[j +1][1])% MOD f[j +1][1]= f[j +1][0]else: f[j +1][0]= f[j +1][1] f[j +1][1]= f[j][0]return f[n][0]# 测试代码if __name__ =="__main__": grid =[[0,1,0],[0,0,1],[1,0,0]] result = uniquePaths(grid)print(result)
在这里插入图片描述

C++完整代码如下:

#include<iostream>#include<vector>usingnamespace std;intuniquePaths(vector<vector<int>>& grid){constint MOD =1'000'000'007;int n = grid[0].size(); vector<vector<int>>f(n +1,vector<int>(2,0)); f[1][0]=1; f[1][1]=1;for(auto& row : grid){for(int j =0; j < n; j++){int x = row[j];if(x ==0){ f[j +1][0]=(f[j][0]+ f[j +1][1])% MOD; f[j +1][1]= f[j +1][0];}else{ f[j +1][0]= f[j +1][1]; f[j +1][1]= f[j][0];}}}return f[n][0];}intmain(){ vector<vector<int>> grid ={{0,1,0},{0,0,1},{1,0,0}};int result =uniquePaths(grid); cout << result << endl;// 输出结果return0;}
在这里插入图片描述

Read more

开箱即用的OCR体验|DeepSeek-OCR-WEBUI支持本地部署与图形化操作

开箱即用的OCR体验|DeepSeek-OCR-WEBUI支持本地部署与图形化操作 1. 引言:让OCR真正“开箱即用” 近年来,光学字符识别(OCR)技术在文档数字化、票据处理、教育扫描等场景中扮演着越来越重要的角色。尽管市面上已有多种OCR解决方案,但大多数依赖云端服务或复杂的环境配置,对普通用户尤其是非技术背景的使用者而言,存在较高的使用门槛。 DeepSeek-OCR-WEBUI 的出现改变了这一现状。作为基于 DeepSeek 开源 OCR 大模型构建的本地化 Web 图形界面工具,它实现了“一键部署 + 可视化操作”的极简体验。无论是金融单据、手写笔记还是模糊图像,用户只需上传文件,即可在浏览器中获得高精度的文字识别结果,全过程无需编写代码、不依赖远程服务器,数据完全保留在本地。 本文将围绕 DeepSeek-OCR-WEBUI 镜像的核心特性、部署流程、关键技术优化以及实际应用建议展开详细解析,帮助开发者和终端用户快速掌握其使用方法与工程价值。 2. 核心功能与技术优势 2.1 模型能力概述 DeepSeek-OCR 是一款由 DeepSeek

字节全员涨薪 35%,L3 年薪 150 万:前端人的“贫富差距”,正在被马太效应彻底拉大...

字节全员涨薪 35%,L3 年薪 150 万:前端人的“贫富差距”,正在被马太效应彻底拉大...

大家好,我是 Sunday。 昨天是 12 月 19 号,周五。原本应该是一个等待放假的好日子😂。但是!整个互联网圈子,尤其是技术圈,被一封邮件彻底炸醒了。 相信大家在群里、朋友圈里都刷屏了:字节跳动全员涨薪。 说实话,当看到这个消息的时候,我就在想:“我当年咋没遇到这么好的时候啊?” 现在很多同学总在说“寒冬”,总在说“降本增效”,总觉得大环境不行了。但字节跳动反手就给了这个观点一记响亮的耳光: 薪资投入提升 35%,调薪投入提升 1.5 倍,L3 职级(原 2-2,大致相当于之前的 阿里 P7)年薪拉高到 90w-150w。 这说明了什么? 这说明,这个行业从来就不缺钱,缺的是值得这笔钱的人。 今天这篇文章,我想把那些新闻通稿撇在一边,单纯从一个技术人、一个教育者的角度,

web的分离不分离:前后端分离与不分离全面分析

web的分离不分离:前后端分离与不分离全面分析

让我们一起走向未来 🎓作者简介:全栈领域优质创作者 🌐个人主页:百锦再@新空间代码工作室 📞工作室:新空间代码工作室(提供各种软件服务) 💌个人邮箱:[[email protected]] 📱个人微信:15045666310 🌐网站:https://meihua150.cn/ 💡座右铭:坚持自己的坚持,不要迷失自己!要快乐 目录 * 让我们一起走向未来 * 一、前后端分离 * 原理 * 优点 * 缺点 * 代码举例(前后端分离): * 二、不分离(传统架构) * 原理 * 优点 * 缺点 * 代码举例(不分离): * 三、总结 在这里插入图片描述 前后端分离与不分离是当前Web开发中两种常见的架构模式。它们各有优缺点,适用于不同的开发需求和场景。 一、前后端分离 原理 前后端分离是指将前端(

glm-4-9b-chat-1m从零部署:vLLM加速+Chainlit前端调用完整流程

glm-4-9b-chat-1m从零部署:vLLM加速+Chainlit前端调用完整流程 想要体验支持百万级上下文长度的强大语言模型吗?GLM-4-9B-Chat-1M不仅能处理约200万中文字符的超长文本,还具备多语言对话、代码执行和工具调用等高级功能。今天我将带你从零开始,一步步部署这个强大的模型,并用简洁美观的Chainlit前端进行调用。 无论你是AI开发者还是技术爱好者,这篇教程都能让你在30分钟内完成整个部署流程,轻松体验超长上下文模型的强大能力。 1. 环境准备与模型部署 在开始之前,确保你的系统满足以下基本要求:至少20GB可用存储空间、16GB以上内存,以及支持CUDA的NVIDIA显卡。推荐使用Ubuntu 20.04或更高版本的系统环境。 1.1 一键部署GLM-4-9B-Chat-1M GLM-4-9B-Chat-1M镜像已经预配置了所有必要的依赖环境,包括vLLM推理引擎和Chainlit前端界面。部署完成后,模型会自动加载并启动服务。 vLLM是专门为大规模语言模型设计的高效推理引擎,它通过PagedAttention等优化技术,显著提升了推