本文涉及知识点
C++动态规划
LeetCode2328. 网格图中递增路径的数目
给你一个 m x n 的整数网格图 grid ,你可以从一个格子移动到 4 个方向相邻的任意一个格子。
请你返回在网格图中从 任意 格子出发,达到 任意 格子,且路径中的数字是 严格递增 的路径数目。由于答案可能会很大,请将结果对 109 + 7 取余 后返回。
如果两条路径中访问过的格子不是完全相同的,那么它们视为两条不同的路径。
示例 1:
输入:grid = [[1,1],[3,4]]
输出:8
解释:严格递增路径包括:
- 长度为 1 的路径:[1],[1],[3],[4] 。
- 长度为 2 的路径:[1 -> 3],[1 -> 4],[3 -> 4] 。
- 长度为 3 的路径:[1 -> 3 -> 4] 。
路径数目为 4 + 3 + 1 = 8 。
示例 2:
输入:grid = [[1],[2]]
输出:3
解释:严格递增路径包括: - 长度为 1 的路径:[1],[2] 。
- 长度为 2 的路径:[1 -> 2] 。
路径数目为 2 + 1 = 3 。
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 1000
1 <= m * n <= 105
1 <= grid[i][j] <= 105
动态规划 网格
动态规划的状态表示
dp[r][c] 表示以单格(r,c)为终点的路径数量。空间复杂度:O(mn)。
动态规划的填报顺序
单格值从小到大枚举后序状态。
动态规划的转移方程
(r,c)的任意临接点(r1,c1)且grid[r1][c1] < gird[r][c]
dp[r][c] += (dp[r1][c1]) ,如果不存在临接点。dp[r][c]为1。
动态规划的初始值
dp全为1。
动态规划的返回值
∑ \sum ∑(dp)
代码
核心代码
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 CGrid {
public:
CGrid(int rCount, int cCount) :m_r(rCount), m_c(cCount) {}
template<class Fun1,class Fun2>
vector<vector<pair<int, int>>> NeiBo(Fun1 funVilidCur, Fun2 funVilidNext, int iConnect = 4)
{
vector<vector<pair<int, int>>> vNeiBo(m_r * m_c);
for (int r = 0; r < m_r; r++)
{
for (int c = 0; c < m_c; c++)
{
if (!funVilidCur(r, c))continue;
auto& v = vNeiBo[Mask(r, c)];
if ((r > 0)&& funVilidNext(r-1, c)) {
v.emplace_back(r-1, c);
}
if ((c > 0) && funVilidNext(r , c - 1)) {
v.emplace_back(r, c - 1);
}
if ((r+1 < m_r ) && funVilidNext(r + 1, c)) {
v.emplace_back(r + 1, c);
}
if ((c+1 <m_c ) && funVilidNext(r, c + 1)) {
v.emplace_back(r, c + 1);
}
}
}
return vNeiBo;
}
vector<vector<int>> Dis( vector<pair<int, int>> start, std::function<bool(int, int)> funVilidCur, std::function<bool(int, int)> funVilidNext, const int& iConnect = 4) {
static short dir[8][2] = { {0, 1}, {1, 0}, {-1, 0},{ 0, -1},{1,1},{1,-1},{-1,1},{-1,-1} };
vector<vector<int>> vDis(m_r, vector<int>(m_c, -1));
for (const auto& [r, c] : start) {
vDis[r][c] = 0;
}
for (int i = 0; i < start.size(); i++) {
const auto [r, c] = start[i];
if (!funVilidCur(r, c)) { continue; }
for (int k = 0; k < iConnect; k++) {
const int r1 = r + dir[k][0];
const int c1 = c + dir[k][1];
if ((r1 < 0) || (r1 >= m_r) || (c1 < 0) || (c1 >= m_c)) { continue; }
if (funVilidNext(r1, c1) && (-1 == vDis[r1][c1])) {
start.emplace_back(r1, c1);
vDis[r1][c1] = vDis[r][c] + 1;
}
}
}
return vDis;
}
void EnumGrid(std::function<void(int, int)> call)
{
for (int r = 0; r < m_r; r++)
{
for (int c = 0; c < m_c; c++)
{
call(r, c);
}
}
}
vector<pair<int, int>> GetPos(std::function<bool(int, int)> fun) {
vector<pair<int, int>> ret;
for (int r = 0; r < m_r; r++)
{
for (int c = 0; c < m_c; c++)
{
if (fun(r, c)) {
ret.emplace_back(r, c);
}
}
}
return ret;
}
inline int Mask(int r, int c) { return m_c * r + c; }
const int m_r, m_c;
const inline static int s_Moves[][2] = { {1,0},{-1,0},{0,1},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1} };
};
class Solution {
public:
int countPaths(vector<vector<int>>& grid) {
const int R = grid.size();
const int C = grid[0].size();
CGrid gr(R, C);
auto fun = [&](int r, int c) {return true; };
auto neiBo = gr.NeiBo(fun, fun);
vector<tuple<int, int, int>> v;
auto Cal = [&](int r, int c) {
v.emplace_back(grid[r][c], r, c);
};
gr.EnumGrid(Cal);
sort(v.begin(), v.end());
vector<vector<C1097Int<>>> dp(R, vector<C1097Int<>>(C, C1097Int<>(1)));
C1097Int<> ans;
for (auto [tmp, r, c] : v) {
for (auto [r1, c1] : neiBo[gr.Mask(r, c)]) {
if (grid[r1][c1] >= tmp)continue;
dp[r][c] += dp[r1][c1];
}
ans += dp[r][c];
}
return ans.ToInt();
}
};
单元测试
vector<vector<int>> grid;
TEST_METHOD(TestMethod11)
{
grid = { {1,1},{3,4} };
auto res = Solution().countPaths(grid);
AssertEx(8, res);
}
TEST_METHOD(TestMethod12)
{
grid = { {1},{2} };
auto res = Solution().countPaths(grid);
AssertEx(3, res);
}
扩展阅读
我想对大家说的话 |
---|
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。 |
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作 |
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注 |
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |
失败+反思=成功 成功+反思=成功 |
视频课程
先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。