1 条题解
-
0
题目理解
我们有一个 的网格,需要在其中放置帐篷。每个帐篷有四个可能的朝向(东、南、西、北),但需要满足严格的朝向约束条件:
- 列约束:同一列中,上方的帐篷必须朝南,下方的帐篷必须朝北
- 行约束:同一行中,左侧的帐篷必须朝东,右侧的帐篷必须朝西
我们需要计算至少放置一个帐篷的所有合法方案数,对 取模。
问题分析
1. 约束条件解读
约束条件实际上限制了:
- 同一列中最多只能有一列连续的帐篷
- 同一行中最多只能有一行连续的帐篷
- 帐篷的朝向由其在行/列中的相对位置完全确定
2. 问题转化
这实际上是一个组合计数问题,我们需要计算在满足上述约束条件下的所有帐篷放置方案。
算法思路
1. 动态规划状态设计
代码使用二维DP:
dp[i][j]表示考虑了前i行,当前行已放置帐篷的列集合大小为j时的方案数。i:当前处理到的行数(从下往上处理)j:当前行中已放置帐篷的列数
2. 状态转移方程
核心转移在代码中体现为:
dp[i - 1][j - 1] = (dp[i][j - 1] + dp[i][j + 1] * (组合数项) + dp[i][j] * (系数项) + dp[i + 1][j] * (系数项)) % MOD;具体分析四种转移:
dp[i][j - 1]:减少一列帐篷dp[i][j + 1] * C(j+1, 2):选择两列合并(组合数)dp[i][j] * (4j):在当前列集合基础上扩展(4种朝向选择)dp[i + 1][j] * (i * j):与下一行的交互
3. 初始化与答案计算
// 初始化:最后一行所有列状态为1 for (i = 0; i <= n; ++i) { dp[i][m] = 1; } // 从下往上递推 for (i = n; i; --i) { for (j = m; j; --j) { // 状态转移 } } // 统计答案:所有第一行的状态 for (i = 0; i < m; ++i) { ans += dp[0][i]; }关键算法细节
1. 处理顺序
采用从下往上、从右往左的递推顺序,确保在计算每个状态时,所需的前置状态都已计算完成。
2. 组合计数技巧
- 使用
(j + 1) * j >> 1计算组合数 - 使用
j << 2表示 ,对应4种朝向选择 - 使用
i * j表示行与列之间的交互方案数
3. 模运算处理
由于结果可能很大,所有运算都对 取模:
dp[i - 1][j - 1] = (...各种项...) % 1000000007;复杂度分析
- 状态数:
- 转移复杂度: 每个状态
- 总复杂度:
- 空间复杂度:
对于 ,复杂度为 ,是可接受的。
算法优化
- 滚动数组:可以优化空间复杂度到
- 寄存器变量:代码中使用
register关键字优化访问速度 - 位运算:使用移位代替除法提高效率
算法标签
- 动态规划:核心算法框架
- 组合数学:方案数计算涉及组合计数
- 计数DP:专门解决计数问题的动态规划
- 模运算:大数取模处理
总结
这道题通过巧妙的动态规划状态设计,将复杂的朝向约束条件转化为可计算的状态转移。算法的核心在于:
- 状态定义:
dp[i][j]精确捕捉了行和列的约束关系 - 转移方程:四种情况完整覆盖了所有可能的帐篷放置方式
- 边界处理:合理的初始化和答案统计
- 优化技巧:寄存器变量、位运算等提升效率
该解法充分利用了动态规划在组合计数问题中的优势,是计数类DP的典型应用。
- 1
信息
- ID
- 5311
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者