当前位置: 首页 > news >正文

LC1931. 用三种不同颜色为网格涂色【经典状态压缩 DP】

1931. 用三种不同颜色为网格涂色

这道题是典型的状态压缩动态规划(状压 DP)。乍一看可能有些复杂,但只要我们抓住题目中m ≤ 5 m \le 5m5这个极其特殊的条件,一步步拆解,思路就会非常清晰。

这种“一宽一窄”的网格问题(n nnm mm小),标准的套路是按列推进,把每一列的颜色组合看作一个整体(状态)

#include<vector>classSolution{public:intcolorTheGrid(intm,intn){intMOD=1e9+7;std::vector<int>valid_states;// 辅助函数:检查单列的状态(三进制数)是否合法(上下相邻颜色不同)autoisValidCol=[&](intmask){intprev=-1;for(inti=0;i<m;++i){intcurr=mask%3;// 提取当前格子的颜色 (0, 1, 2)if(curr==prev)returnfalse;prev=curr;mask/=3;}returntrue;};// 1. 找出单列的所有合法状态for(inti=0;i<pow(3,m);++i){if(isValidCol(i)){valid_states.push_back(i);}}// 辅助函数:检查两个合法状态能否相邻(左右相邻颜色不同)autoisValidAdj=[&](intmask1,intmask2){for(inti=0;i<m;++i){if(mask1%3==mask2%3)returnfalse;mask1/=3;mask2/=3;}returntrue;};intnum_states=valid_states.size();std::vector<std::vector<int>>adj(num_states);// 2. 预处理状态转移图// adj[i] 中存储的是可以排在状态 i 后面的所有合法状态的索引for(inti=0;i<num_states;++i){for(intj=0;j<num_states;++j){if(isValidAdj(valid_states[i],valid_states[j])){adj[i].push_back(j);}}}// 3. 动态规划// dp 数组记录到达当前列时,各个状态的方案数std::vector<longlong>dp(num_states,1);// 从第 2 列循环到第 n 列for(intcol=1;col<n;++col){std::vector<longlong>next_dp(num_states,0);for(inti=0;i<num_states;++i){for(intj:adj[i]){// 状态 i 可以转移到状态 jnext_dp[j]=(next_dp[j]+dp[i])%MOD;}}dp=next_dp;}// 4. 汇总最后一列所有状态的方案数longlongans=0;for(longlongcount:dp){ans=(ans+count)%MOD;}returnans;}};

算法思路

1. 状态表示与压缩(把一列变成一个数字)

因为有三种颜色(红、绿、蓝),我们自然可以把它们映射为数字012

一列有m mm个格子,我们完全可以把这一列的颜色组合看作一个m mm位的三进制数

2. 列内合法性筛选(排除上下同色)

并非所有的三进制数都是合法的。题目要求相邻的格子颜色不能相同,这意味着一列之内,上下相邻的格子颜色必须不同

3. 列间合法性预处理(建立状态转移图)

现在我们有了所有合法的单列组合。接下来要解决跨列的问题:把两列拼在一起时,左右相邻的格子颜色也不能相同

4. 动态规划递推(核心推导)

准备工作做完,终于可以开始涂色了。我们从左到右,一列一列地涂。

最后,当循环跑到第n nn列时,我们把第n nn列所有合法状态对应的方案数全部加起来(并对10 9 + 7 10^9 + 7109+7取模),就是整个网格的涂色方法总数了。

时空复杂度分析

由于这道题有两个独立维度的输入变量:行数m mm和列数n nn,我们的分析需要同时包含这两个变量。

为了让推导更清晰,我们先定义一个核心中间变量:单列的合法状态总数,记为∣ S ∣ |S|S


时间复杂度分析

算法的执行可以分为三个明确的阶段,我们逐一计算:

  1. 寻找单列合法状态:

    我们需要遍历所有可能的三进制状态(共3 m 3^m3m种),并对每一个状态花费O ( m ) \mathcal{O}(m)O(m)的时间检查其上下相邻颜色是否合法。

  2. 预处理状态转移图(建图):

    我们需要两两比对所有合法的状态,判断它们能否左右相邻。合法的状态有∣ S ∣ |S|S个,因此要进行∣ S ∣ 2 |S|^2S2次比对。每次比对需要遍历m mm个格子。

  3. 动态规划(DP)递推:

    这是算法的主循环。我们需要从第 2 列遍历到第n nn列,共进行n − 1 n-1n1次迭代。

    在每一次迭代中,我们要遍历所有∣ S ∣ |S|S个状态,并查找它能转移到的后续状态。一个合法状态的后续合法状态数量,理论上最多不超过2 m 2^m2m种(因为每一行的右侧格子只有 2 种可用颜色)。所以每次列推导的计算次数受限于∣ S ∣ ⋅ 2 m |S| \cdot 2^mS2m(或者粗略写成不超过∣ S ∣ 2 |S|^2S2)。

综合时间复杂度:

将上述三部分相加为O ( m ⋅ 3 m + m ⋅ ∣ S ∣ 2 + n ⋅ ∣ S ∣ ⋅ 2 m ) \mathcal{O}(m \cdot 3^m + m \cdot |S|^2 + n \cdot |S| \cdot 2^m)O(m3m+mS2+nS2m)

代入∣ S ∣ = 3 × 2 m − 1 |S| = 3 \times 2^{m-1}S=3×2m1,并忽略常数项后,最大的主导项显然是 DP 递推部分。

因此,最终的时间复杂度为:

O ( n ⋅ 4 m ) \mathcal{O}(n \cdot 4^m)O(n4m)

现实执行情况:m = 5 m=5m=5n = 1000 n=1000n=1000时,n ⋅ 4 5 ≈ 1000 × 1024 ≈ 10 6 n \cdot 4^5 \approx 1000 \times 1024 \approx 10^6n451000×1024106次基本操作。这在现代计算机中连几毫秒都用不到,效率极高。


空间复杂度分析

空间开销主要取决于我们在内存中存储了哪些结构:

  1. 合法状态数组 (valid_states):

    存储所有合法的单列状态。

  2. 状态转移邻接表 (adj):

    对于∣ S ∣ |S|S个状态中的每一个,存储它可以转移到的目标状态。如上文分析,总的边数(转移关系数)最多为∣ S ∣ ⋅ 2 m |S| \cdot 2^mS2m

  3. 动态规划数组 (dpnext_dp):

    因为我们使用了滚动数组优化,不再需要开辟n × ∣ S ∣ n \times |S|n×S的二维矩阵,只需要两个长度为∣ S ∣ |S|S的一维数组交替使用。

综合空间复杂度:

主导项是用于存储状态转移关系的邻接表,因此最终的空间复杂度为:

O ( ∣ S ∣ ⋅ 2 m ) ≈ O ( 4 m ) \mathcal{O}(|S| \cdot 2^m) \approx \mathcal{O}(4^m)O(S2m)O(4m)

现实执行情况:m = 5 m=5m=5时,空间消耗大致是4 5 = 1024 4^5 = 102445=1024个整数类型的内存,这不到几十 KB,空间复杂度可以说是无限接近于O ( 1 ) \mathcal{O}(1)O(1)的极小常数。

需要了解在n nn极大(比如n = 10 9 n = 10^9n=109)的情况下,如何用矩阵快速幂将时间复杂度从O ( n ⋅ 4 m ) \mathcal{O}(n \cdot 4^m)O(n4m)进一步压缩到O ( log ⁡ n ⋅ 8 m ) \mathcal{O}(\log n \cdot 8^m)O(logn8m)吗?

http://www.jsqmd.com/news/539989/

相关文章:

  • 论文省心了!盘点2026年断层领先的AI论文平台
  • nli-distilroberta-base真实效果:支持batch推理,吞吐量达128句/秒(T4 GPU)
  • Claude Code 进阶功能全解析
  • Copilot: 如何把kiro的spec转到leanSpec来
  • 5个实战秘诀:轻松掌握开源咖啡烘焙软件Artisan
  • 从XML解析到特征提取:手把手搞定Wikipedia多模态数据集预处理(附VGG16/Doc2Vec代码)
  • Ubuntu 20.04上RealVNC Server的3种运行模式详解:虚拟、服务、用户模式怎么选?
  • VOOHU 沃虎电子 | 电流互感器选型指南:匝数比、初级电流与隔离电压怎么选?
  • ClawLink:AI Agent 社交网络 —— 让你的数字分身真正“联网”
  • 如何掌握ComfyUI IPAdapter Plus:三步实现精准图像风格迁移
  • LVGL8中文界面开发实战:从字库生成到GUI Guider配置全流程
  • Claude自动化教程,Claude深夜偷爬你的微信:零API纯视觉秒回99+群聊,Mac已沦陷!
  • 降AI工具千字4.8元贵不贵?嘎嘎降AI性价比全面分析
  • 用户画像3步法:属性+行为+动机,精准锁定客户需求-佛山鼎策创局破局增长咨询
  • 【图像加密解密】交替量子漫步的量子彩色图像加密解密【含Matlab源码 15222期】含参考文献
  • 虚幻引擎资源解锁神器:UModel从入门到精通的实战指南
  • 告别用人“开盲盒”|江湖背调定义全生命周期风控范式
  • 工业智能化改造的Java技术落地路径:从场景突破到B端定制开
  • 告别云依赖:HomeAssistant-GreeClimateComponent实现本地化智能空调控制
  • 2026年数控柔性折弯中心哪家强?直销厂家评测揭晓,市面上折弯中心供应商推荐企业引领行业技术新高度 - 品牌推荐师
  • ESP32无人机远程识别系统架构设计与安全实现深度解析
  • 实战详解:vmware虚拟机usb设备不识别怎么办?硬件级网络透传全流程与API集成
  • YOLOv8改进:MixUp with Consistency——基于混合增强与一致性正则化的鲁棒性目标检测算法
  • VOOHU 沃虎电子 双口堆叠非集成式RJ45连接器 SYT59212188HWA1DY1A022短体 灵活选配网络变压器 适用于高密度交换机与工业设备
  • Topit:提升Mac多任务效率的窗口置顶解决方案
  • 2026年AI Agent爆发:从ChatGPT到自主智能体的进化之路
  • XMC芯片代理-XMC武汉新芯代理商-XMC(武汉新芯)SPI NOR Flash存储芯片代理公司
  • 汽车智能制造时代,哪些服务商助力智慧供应链?
  • CSS:实现带描边的对话气泡框
  • Linux 内存管理总结