【C++动态规划 网格】2328. 网格图中递增路径的数目|2001

本文涉及知识点

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++**实现。

你可能感兴趣的:(c++,动态规划,算法,leetcode,网格,数目,递增)