牛客(美团)- 拜访(动态规划)

题目描述

现在有一个城市销售经理,需要从公司出发,去拜访市内的商家,已知他的位置以及商家的位置,但是由于城市道路交通的原因,他只能在左右中选择一个方向,在上下中选择一个方向,现在问他有多少种方案到达商家地址。

给定一个地图map及它的长宽n和m,其中1代表经理位置,2代表商家位置,-1代表不能经过的地区,0代表可以经过的地区,请返回方案数,保证一定存在合法路径。保证矩阵的长宽都小于等于10。

测试样例:
[[0,1,0],[2,0,0]],2,3
返回:2

注:题目的意思不够清晰,所谓的上中下选一种实际是在上下走向上要求往上走了就只能一路向上了,不可再向下了,但左右不管。但对于左中右选一种则就是管水平走向,走了右便不再有任何往左走的机会了,同理上下它不管。
再直白一点就是只有四种走向,右上走(包括只往右走或只往上走,之后同理)、右下走、坐上走、左下走。

思路

1、先确定起点和终点的位置,保存到变量中。

2、处理特殊情况,起点与终点同位置(之后想想感觉不存在,因为一个元素不可能既赋予1值又赋予2值,它只可能是一个值)

3、给边缘路径赋值(动态规划需要用到之前的值来推出后面的值)

  • 边缘值只可能是1或0,值代表有多少条路径会经过次数,因为不可往反方向走,所以边缘要么有一条路过这里,要么一条路都么有。
  • 比如,从左上走到右上,只能从左一直往右走一条路,或者因为不通无法前进。不可能出现我先下去,然后再往上走到这个边缘来。
  • 这里的边缘不是指地图的边缘,而是指以起点和终点为两个顶点组成的矩形(也可能是一条直线)的边缘。
    在这里插入图片描述
  • 比如此图中,上述所指的边缘是在红色框内部靠着红色框的行和列
  • 边缘赋值是在一个新建的二维数据dp[][]来保存的。的方式是,如果路不通(map[][]值为-1)则赋值为0,在通的情况下(map[][]值为0)赋值为其前一个位置的值(也就是1)。
  • 这个用于保存经过路径数二维数组起点值初始设置为1(拿此图来说就是dp[0][2]=1),其余默认值0。
  • 比如这里的起点1往下走是0通的,那么对应的这个位置dp[1][2]赋值为上一个位置(它的上一个是起点)的值dp[0][2]也就是1,接着往下走也是0,赋值为上一个位置的值1,依次类推走到与终点平齐的那一行结束。往右同理。

4、边缘完成后便是内部赋值。

  • 用此实例来说明,用两个for循环,从起点1的右下位置开始,即起点横坐标加1纵坐标加1的位置开始。
  • 此位置的值为其左边和上边的值的和,原理很简单,假设这个位置的左边一个位置值为1(即有一条路通过左边这个位置),它上面的位置的值为0。那么通过该位置的路径就是1了,因为有一条左边过来的路径可以经过它,上面没有过来的路径。(动态规划,通过前面的答案推出后面的,并考虑到所有情况)

5、赋值完后,最终终点的赋值就是答案了。

代码

public static int countPath(int[][] map, int n, int m) 
{
    // 首先找出起点(公司)和终点(商家)的位置
    int startx = 0, starty = 0, endx = 0, endy = 0;
    	
    for (int i = 0; i < n; i++)
    {
    	for (int j = 0; j < m; j++)
    	{
    		if (map[i][j] == 1)
    		{
    			startx = i;
    			starty = j;
    		}
    		else if (map[i][j] == 2)
    		{
    			endx = i;
    			endy = j;
    		}
    	}
    }
    	
    // 两点重合,那么只有原地不动一种路径走法
    if (startx == endx && starty == endy) return 1;
    // 用于记录可以经过此点的路径个数
    int dp[][] = new int[n][m];
    	
    // 列数对应左右走向,行数对应上下走向
    // 根据起点与终点坐标的差值,来判断往哪个方向走
    int leftorRight = endy - starty > 0 ? 1 : -1;	// >0往右走(i+),<0往左走(i-)
    int uporDown = endx - startx > 0 ? 1 : -1;		// >0往下走(y+),<0往上走(y-)
    	
    dp[startx][starty] = 1;	// 起点的路径数设置为1
     // 行数从起点往终点的方向请进一步(水平方向前进一步)的位置开始循环	
    int row = startx + uporDown;
    while ((row - endx) * uporDown <= 0)
    {	// 如果不同,此点的路径数就置0,通则置为前一个位置的路径数(边缘也只会是1)
    	dp[row][starty] = map[row][starty] == -1 ? 0 : dp[row - uporDown][starty];
    	row += uporDown;
    }
   	// 列数为从起点到终点的方向前进一步(垂直方向)的位置开始循环	
    int column = starty + leftorRight;
    while ((column - endy) * leftorRight <= 0)
    {	
    	dp[startx][column] = map[startx][column] == -1 ? 0 : dp[startx][column - leftorRight];
    	column += leftorRight;
    }
    // 重置循环的起点横行数
    row = startx + uporDown;
    while ((row - endx) * uporDown <= 0)
    {	// 重置循环起点的列数
    	column = starty + leftorRight;
    	while((column - endy) * leftorRight <= 0)
    	{	// 这里开始给内部赋值(将用上边缘的值和之前的内部值)
    		dp[row][column] = map[row][column] == -1 ? 0 : dp[row - uporDown][column] + dp[row][column - leftorRight];
    		column += leftorRight;
    	}
    	row += uporDown;
    }	
    return dp[endx][endy];
}

注:参考牛客上大佬的思路,其个人主页https://www.nowcoder.com/profile/587889

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
动态规划在解决牛客删除括号问题时,可以按照以下步骤进行: 1. 首先,我们需要理解题目的需求。题目要求我们删除括号,使得剩下的字符串满足以下条件:左括号和右括号的数量相等,且左括号的位置必须在右括号的前面。 2. 接下来,我们可以使用动态规划来解决这个问题。我们可以定义一个三维的dp数组,其中dp[q][w][e]表示考虑s前q个字符,t前w个字符,且s被删除部分左括号数减去右括号数为e时,是否可行(bool类型)。 3. 然后,我们可以从前向后遍历字符串s和t。在每一步中,我们可以考虑两种情况: a. 删除的左括号数目比右括号多:我们可以继续删除左括号,或者删除右括号。即dp[q][w][e] = dp[q-1][w][e+1]或dp[q-1][w][e-1]。 b. 删除的左括号数目与右括号数目相同:我们只能删除右括号。即dp[q][w][e] = dp[q-1][w-1][e-1]。 4. 最后,我们可以根据dp[len1][len2][0]的值来判断是否可行。其中len1和len2分别表示字符串s和t的长度。 综上所述,通过动态规划的思路,我们可以解决牛客删除括号的问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [动态规划笔记](https://download.csdn.net/download/weixin_38617297/13751806)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [牛客_21303删括号_动态规划](https://blog.csdn.net/weixin_45619006/article/details/114650172)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值