unique-paths(不同路径)递归+动归的做法

https://www.nowcoder.com/practice/166eaff8439d4cd898e3ba933fbc6358?tpId=46&tqId=29117&tPage=1&rp=1&ru=/ta/leetcode&qru=/ta/leetcode/question-ranking

 

思路:动态规划,很明显在(x,y)到终点的位置的方法数目就是(x, y + 1)【向右走到的点去终点的路径数目】 +  (x + 1, y)【向下走到的点去终点的路径数目】

 

首先一个超时的递归版本


class Solution {
public:
    int uniquePaths(int m, int n) {
        int res = solve(0 , 0, m ,n );
		return res;
    }
	int solve(int x, int y, int m, int n)
	{
		if(x == m - 1 &&y == n - 1)//走到终点了
			return 1;
		if(x <0 || x > m || y < 0 || y > n)//越界了
			return 0;
		//就是右边点到终点的路径数+下边点到终点的路径数
		return solve(x + 1, y, m, n) + solve(x, y + 1, m, n );
	}
};

显然对上面之所以超时是因为有一些点到终点的方法数重复去求解了,比如求(0,1)到终点(3,3)的方法数时,其下角的点(1,2)到终点的方法数求一次((0 ,1 )先走到右边 (0, 2 ),(0,2)走下面(1 , 2 ))

然后再求(0,2)到终点方法数目时候,走下边(1,2),(1, 2)又被求了一次。诸如这样重复计算的点还有很多

所以可以在求到每次的结果的时候可以把结果先储存起来。就是记忆化递归了。

记忆化递归版本:

class Solution {
public:
	int uniquePaths(int m, int n) {
		//储存(x, y)到终点路径数量
		vector<vector<int > >f (m +1, vector<int>(n + 1) );
		//标记(x, y)到终点路径数量是否已经求出来了
		vector<vector<bool> >mark(m + 1, vector<bool>(n + 1) );
		//求解
		int res = solve(0 , 0, m ,n , f, mark);
		return res;
    }

	int solve(int x, int y, int m, int n,  vector<vector<int> > & f, vector<vector<bool> > & mark)
	{
		if(x == m - 1 && y == n - 1)//走到终点了
		{
			mark[x][y] = true;//标记该点到终点路径数目已经求出来了
			f[x][y] = 1;//储存结果
			return 1;
		}
		if(x <0 || x > m || y < 0 || y > n)//越界了
		{
			return 0;
		}
		
		
		if(x +1 >= 0 && x +1 < m && !mark[x +1][y])//在范围内且没被求过
		{
			f[x + 1][y] = solve(x +1, y, m, n, f, mark);//储存结果
			mark[x +1][y] = true;//标记

		}
		if(y + 1 >= 0 && y + 1 < n && !mark[x][y +1])//同上
		{
			f[x][y + 1] = solve(x, y + 1, m, n, f, mark);
			mark[x][y + 1] = true;
		}
		return f[x + 1][y] + f[x][ y + 1]; //就是右边的点到终点的路径数+下边的点到终点的路径数
		

	}
};

 

动态规划版本。其实是记忆化递归的逆过程。


 class Solution {
public:
    int uniquePaths(int m, int n) {
        if(m <= 0 ||n <= 0)
			return 0;
		vector<vector<int> >f(m, vector<int>(n));//用向量表示二维数组m*n
		//也可以用下面的动态申请二维数组来表示m*n数组

		/*
		int ** f;
		f = new int*[m +1];
		for(int i = 0; i < m +1; i++)
			f[i] = new int[n +1];
		*/

		for(int i = 0; i < m ; i++)//初始化所有的点到终点路径数目
		{
			for(int j = 0; j < n  ;j++)
				f[i][j] = 1;
		}

		for(int j = 0; j < n - 1; j++)//初始化底边到终点路径数目
			f[m-1][j] = 1;
		for(int i = 0; i < m - 1 ;i++)//初始化最右边到终点路径数目
			f[i][n - 1] = 1;
		
		
		for(int i = m - 2; i >= 0; i--)
		{
			for(int j = n - 2; j >= 0; j--)
				f[i][j] = f[i +1][j] + f[i][j + 1];//(i,j)到终点路径数就是其右边的点到终点的路径数 + 其下面的点到终点的路径数
		}
		return f[0][0];//起点到终点的路径数目
	
    }
	
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值