leetcode 1301. 最大得分的路径数目

Posted 大忽悠爱忽悠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode 1301. 最大得分的路径数目相关的知识,希望对你有一定的参考价值。

在这里插入图片描述
在这里插入图片描述


最大得分的路径数目题解集合


记忆化搜索–DFS

  • 首先我们来看看递归的结束条件应该是什么:

在这里插入图片描述

  • 再来看看如何求解当前位置的最大贡献值:

注意:虽然这里是从右下角到达左上角,但实际上还是属于自下而上的递归,相当于求终点开始到达起点的最大贡献值,那么此时对于任意一个位置(i,j)来说,只有它的左边,左上和上方可以到达该位置

在这里插入图片描述

  • 由此得出递归三部曲:
    1.递归结束条件:到达终点,遇到障碍物,越过边界
    2,本级递归做什么:求解当前位置最大贡献值
    3,返回值:返回当前位置最大贡献值

但是注意本题还要求求出最大贡献值的方案数,如何求解呢?
这里大家就直接看下面代码即可理解
代码:

class Solution {
	map<pair<int, int>, pair<int, int>> ret;//缓存器,用来保存当前位置最大贡献值和得到最大贡献值的方案数量
	int mod = 1e9 + 7;
public:
	vector<int> pathsWithMaxScore(vector<string>& board)
	{
		int r = board.size();
		int s= dfs(board, r - 1, r - 1).first;
		int n = dfs(board, r - 1, r - 1).second;
		return {s,n};
	}
	pair<int,int> dfs(vector<string>& board, int i, int j)
	{
		//当前位置的结果已经计算得出
		if (ret.find({ i,j }) != ret.end()) return ret[{i, j}];
		//越界
		if (i < 0 || j < 0 || i >= board.size() || j >= board.size())
			return { 0,0 };
		//如果当前位置到达终点,那么终点的最大贡献值为0,到达终点说明得到一个方案
		if (i==0&&j==0)
			return {0,1};
		//如果当前位置遇到障碍物,那么当前位置的最大贡献值为0,方案数为0,因为当前路径为无效路径
		if (board[i][j] == 'X')
			return {0,0};
		int s1 = dfs(board, i, j - 1).first;
		int s2 = dfs(board, i - 1, j).first;
		int s3 = dfs(board, i - 1, j - 1).first;
		int n1 = dfs(board, i, j - 1).second;
		int n2 = dfs(board, i - 1, j).second;
		int n3 = dfs(board, i - 1, j - 1).second;
		//先计算出三个方向中的最大贡献值
		int maxScore = getmax(s1, s2, s3);
		//计算出三个方向的最大方案数
		int maxNum = getmax(n1, n2, n3);
		//如果三个方向的最大方案数不为0,说明可以到达这个(i,j)坐标,我们就把最大的分数和当前格子的分数相加
		if (maxNum)
		{
			if (board[i][j] == 'S')
				ret[{i, j}].first = maxScore % mod;
			else
			ret[{i, j}].first = (maxScore + board[i][j] - '0') % mod;
		}
		else
			ret[{i, j}].first = 0;
		int num = 0;//方案数量
		//看看是否有不同的方式得到同样的最大贡献值
		if (maxScore == s1)
		{
			num = (n1 + num) % mod;
		}
		if (maxScore == s2)
		{
			num = (n2 + num) % mod;
		}
		if (maxScore == s3)
		{
			num = (n3 + num) % mod;
		}
		ret[{i, j}].second = num % mod;
		return ret[{i, j}];
	}
	int getmax(int a, int b, int c)
	{
		return max(a, max(b, c));
	}
};

在这里插入图片描述


动态规划

思路:
其实就是对上面记忆化搜索的翻译过程,这里不做详细解释,大家可以看下面的代码思考
但这里注意要对边界的值要提前求出来,作为最小子问题

注意:字符和整数相加得出整数的方法: 3 + board[0][1] - '0'

	vector<string> board =
	{
		"E23","2X2","12S"
	};
	cout << board[0][1] - '0' << endl;
	cout<<3 + board[0][1] - '0' << endl;
	cout << 3 + board[0][1] << endl;
	cout << 3 + '5' - '0' << endl;

在这里插入图片描述

代码:

class Solution {
/**
dp[i][j].first 为(i,j)位置的最大得分
dp[i][j].second 为(i,j)位置的最大得分方案数
我们可以从'E'出发到'S' 就是从一个格子能往右、下和右下三个方向走,即能从三个格子到达(i,j)坐标
**/
public:
	vector<int> pathsWithMaxScore(vector<string>& board) 
	{
		int mod = 1e9 + 7;
		int n = board.size();
		vector<vector<pair<int, int>>> dp(n, vector<pair<int, int>>(n, {0,0}));//都初始化为0 0
		//起点的最大得分为0,方案数为1
		dp[0][0].second = 1;
		//处理边界
		for (int i = 1; i < n; ++i)
		{
			//如果边界上没有出现障碍物,那么就可以一直走下去,一旦出现障碍物就无法继续走下去了
			if (board[i][0] != 'X' && board[i - 1][0] != 'X')
			{
				//第一列上每一个位置的路径和等于它前面一个的路径和加上自身
				dp[i][0].first += (board[i][0] - '0' + dp[i - 1][0].first) % mod;
				//方案数等于前面一个的方案数
				dp[i][0].second = dp[i - 1][0].second % mod;
			}
			if (board[0][i] != 'X' && board[0][i - 1] != 'X')
			{
				//第一行上每一个位置的路径和等于它前面一个的路径和加上自身
				dp[0][i].first += (board[0][i] - '0' + dp[0][i-1].first) % mod;
				//方案数等于前面一个的方案数
				dp[0][i].second = dp[0][i-1].second % mod;
			}
		}
		int curnum;//当前格子的分数
		int curmax;//可以从三个方向(格子)走到(i,j) 这个curmax就是三个方向(格子)当前最大的得分
		int curstep;//可以从三个方向(格子)走到(i,j) 这个curmax就是三个方向(格子)当前的方案数
		for (int i = 1; i < n; ++i)
		{
			for (int j = 1; j < n; ++j)
			{
				char cur = board[i][j];
				if (cur == 'X')//如果当前格子放置了障碍物,那么不管,障碍物处的路径和默认为0,方案数为0
					continue;
				if (cur== 'S')//如果当前格子为'S'的话分数为0
					curnum = 0;
				else
					curnum = board[i][j] - '0';//获得当前格子的分数
				curmax = getmax(dp[i - 1][j].first, dp[i][j - 1].first, dp[i - 1][j - 1].first);//获取三个方向的最大得分
				curstep = getmax(dp[i - 1][j].second, dp[i][j - 1].second, dp[i - 1][j - 1].second);//获取三个方向的最大方案数
				if (curstep)//如果三个方向的最大方案数不为0,说明可以到达这个(i,j)坐标,我们就把最大的分数和当前格子的分数相加
					dp[i][j].first = (curmax + curnum) % mod;
				//下面三个if是把最大分数为curmax的方向的方案数累加到(i,j)的方案数
				if (dp[i - 1][j].first == curmax)
					dp[i][j].second = (dp[i][j].second + dp[i - 1][j].second) % mod;
				if (dp[i][j - 1].first == curmax)
					dp[i][j].second = (dp[i][j].second + dp[i][j - 1].second) % mod;
				if (dp[i - 1][j - 1].first == curmax)
					dp[i][j].second = (dp[i][j].second + dp[i - 1][j - 1].second) % mod;

			}
		}
		return { dp[n - 1][n - 1].first,dp[n - 1][n - 1].second };//返回最后一个格子的分数和方案数
	}
	int getmax(int a, int b, int c) 
	{
		return max(a, max(b, c));
	}
};

在这里插入图片描述


总结

本题难度方面还是比较大的,我的功力尚浅可能无法解释透彻,如果不理解可以私信我

以上是关于leetcode 1301. 最大得分的路径数目的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode﹝堆ி﹞移除石子的最大得分,吃苹果的最大数目等

[编程题]得分最大

LeetCode 1422. 分割字符串的最大得分

LeetCode 1753. 移除石子的最大得分

LeetCode 1753. 移除石子的最大得分

LeetCode 1422 分割字符串的最大得分[贪心] HERODING的LeetCode之路