本文涉及知识点
C++动态规划
LeetCode2435. 矩阵中和能被 K 整除的路径
给你一个下标从 0 开始的 m x n 整数矩阵 grid 和一个整数 k 。你从起点 (0, 0) 出发,每一步只能往 下 或者往 右 ,你想要到达终点 (m - 1, n - 1) 。
请你返回路径和能被 k 整除的路径数目,由于答案可能很大,返回答案对 109 + 7 取余 的结果。
示例 1:
输入:grid = [[5,2,4],[3,0,5],[0,7,2]], k = 3
输出:2
解释:有两条路径满足路径上元素的和能被 k 整除。
第一条路径为上图中用红色标注的路径,和为 5 + 2 + 4 + 5 + 2 = 18 ,能被 3 整除。
第二条路径为上图中用蓝色标注的路径,和为 5 + 3 + 0 + 5 + 2 = 15 ,能被 3 整除。
示例 2:
输入:grid = [[0,0]], k = 5
输出:1
解释:红色标注的路径和为 0 + 0 = 0 ,能被 5 整除。
示例 3:
输入:grid = [[7,3,4,9],[2,3,6,2],[2,3,7,0]], k = 1
输出:10
解释:每个数字都能被 1 整除,所以每一条路径的和都能被 k 整除。
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 5 * 104
1 <= m * n <= 5 * 104
0 <= grid[i][j] <= 100
1 <= k <= 50
动态规划
动态规划的状态表示
dp[r][c][k] 记录从起点到达(r,c) 路径和%K == k的方案数。空间复杂度:O(mnk)
动态规划的转移方程
枚举前置状态(r,c,k)
(r1,c1)是(r+1,c)或(r,c+1)
dp[r1][c1][(k+grid[r1][c1])%K] += dp[r][c][k]
单个状态转移的时间复杂度是:O(1),** 总复杂度**:O(mnk)
动态规划的填表顺序
先行后列,行号和列号都从小到大。
由于只能下移,不能上移,所以行号小的一定是前置节点。由于只能右移,不能左移,所以行号相同,列号小的是前置节点。
动态规划的初始值
全部为0。
动态规划的返回值
dp.back().back()[0]
代码
核心代码
template<int MOD = 1000000007>
class C1097Int
{
public:
C1097Int(long long llData = 0) :m_iData(llData% MOD)
{
}
C1097Int operator+(const C1097Int& o)const
{
return C1097Int(((long long)m_iData + o.m_iData) % MOD);
}
C1097Int& operator+=(const C1097Int& o)
{
m_iData = ((long long)m_iData + o.m_iData) % MOD;
return *this;
}
C1097Int& operator-=(const C1097Int& o)
{
m_iData = (m_iData + MOD - o.m_iData) % MOD;
return *this;
}
C1097Int operator-(const C1097Int& o)
{
return C1097Int((m_iData + MOD - o.m_iData) % MOD);
}
C1097Int operator*(const C1097Int& o)const
{
return((long long)m_iData * o.m_iData) % MOD;
}
C1097Int& operator*=(const C1097Int& o)
{
m_iData = ((long long)m_iData * o.m_iData) % MOD;
return *this;
}
C1097Int operator/(const C1097Int& o)const
{
return *this * o.PowNegative1();
}
C1097Int& operator/=(const C1097Int& o)
{
*this /= o.PowNegative1();
return *this;
}
bool operator==(const C1097Int& o)const
{
return m_iData == o.m_iData;
}
bool operator<(const C1097Int& o)const
{
return m_iData < o.m_iData;
}
C1097Int pow(long long n)const
{
C1097Int iRet = 1, iCur = *this;
while (n)
{
if (n & 1)
{
iRet *= iCur;
}
iCur *= iCur;
n >>= 1;
}
return iRet;
}
C1097Int PowNegative1()const
{
return pow(MOD - 2);
}
int ToInt()const
{
return m_iData;
}
private:
int m_iData = 0;;
};
class Solution {
public:
int numberOfPaths(vector<vector<int>>& grid, int K) {
const int R = grid.size();
const int C = grid[0].size();
vector<vector<vector<C1097Int<>>>> dp(R, vector<vector<C1097Int<>>>(C, vector<C1097Int<>>(K)));
dp[0][0][grid[0][0] % K] = 1;
for (int r = 0; r < R; r++) {
for (int c = 0; c < C; c++) {
for (int k = 0; k < K; k++) {
if (r + 1 < R) {
dp[r + 1][c][(k + grid[r + 1][c]) % K] += dp[r][c][k];
}
if (c + 1 < C) {
dp[r][c + 1][(k + grid[r][c + 1]) % K] += dp[r][c][k];
}
}
}
}
return dp.back().back()[0].ToInt();
}
};
单元测试
vector<vector<int>> grid;
int k;
TEST_METHOD(TestMethod11)
{
grid = { {5,2,4},{3,0,5},{0,7,2} }, k = 3;
auto res = Solution().numberOfPaths(grid, k);
AssertEx(2, res);
}
TEST_METHOD(TestMethod12)
{
grid = { {0,0} }, k = 5;
auto res = Solution().numberOfPaths(grid, k);
AssertEx(1, res);
}
TEST_METHOD(TestMethod13)
{
grid = { {7,3,4,9},{2,3,6,2},{2,3,7,0} }, k = 1;
auto res = Solution().numberOfPaths(grid, k);
AssertEx(10, res);
}