LeetCode刷题第四天

LeetCode刷题记录
1.机器人的运动范围
地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?
思路:
1).深度优先遍历DFS
-深度优先搜索:先暴力法模拟机器人在矩阵中的所有路径,DFS通过递归,先朝一个方向搜到底,再回溯至上个节点(其实只需要标记这个格子被访问过了,不用回溯,因为每个格子只能走一次),沿另一个方向搜索,以此类推。
-剪枝:在搜索中,遇到数位和超出目标值、此元素已访问,则应立即返回,称之为可行性剪枝。
2).广度优先遍历BFS
-BFS/DFS:两者目标都是遍历整个矩阵,不同点在于搜索顺序不同。DFS是朝一个方向走到底,再回退,以此类推,BFS则是按照“平推”的方式向前搜索。
-BFS实现:通常利用队列实现广度优先遍历。

题解:

#(1)DFS
class Solution:
	def movingCount(self, m:int, n:int, k:int):
		def dfs(i, j, si, sj):
			if i >= m or j >= n or k < si + sj or (i, j) in visited: return 0
			visited.add((i, j))
			return 1 + dfs(i+1, j, si+1 if (i+1) % 10 else si -8, sj) + dfs(i, j+1, si, sj+1 if (j+1) % 10 else sj-8)
		visited = set()
		return dfs(0, 0, 0, 0)

#(2)BFS
class Solution:
	def movingCount(self, m:int, n:int, k:int):
	queue, visited = [(0, 0, 0, 0)], set()
	while queue:
		i, j, si, sj = queue.pop(0)
		if i >= m or j >= n or k < si + sj or (i, j) in visited:continue
		visited.add((i, j))
		queue.append((i+1, j, si+1 if (i+1) % 10 else si -8, sj))
		queue.append((i, j+1, si, sj+1 if (j+1) % 10 else sj-8))
	return len(visited)

注意:BFS队列实现广度优先遍历还要去思考下。

2.剪绳子
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]…*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

思路:

  1. 数学推导
    当 n≤3 时,按照规则应不切分,但由于题目要求必须剪成 m>1 段,因此必须剪出一段长度为 1的绳子,即返回 n - 1 。
    当 n>3时,求 n 除以 3 的 整数部分a 和 余数部分 b (即 n = 3a + b),并分为以下三种情况:
    -当 b = 0 时,直接返回 3^a;
    -当 b = 1 时,要将一个 1+3 转换为 2+2,因此返回 3^{a-1)×4;
    -当 b = 2时,返回 3^a×2

  2. 贪心算法
    如果 n == 2,返回1,如果 n == 3,返回2,两个可以合并成n小于4的时候返回n - 1
    如果 n == 4,返回4
    如果 n > 4,分成尽可能多的长度为3的小段,每次循环长度n减去3,乘积res乘以3;最后返回时乘以小于等于4的最后一小段
    以上2和3可以合并

  3. 动态规划
    (1) 我们想要求长度为n的绳子剪掉后的最大乘积,可以从前面比n小的绳子转移而来
    (2) 用一个dp数组记录从0到n长度的绳子剪掉后的最大乘积,也就是dp[i]表示长度为i的绳子剪成m段后的最大乘积,初始化dp[2] = 1
    (3) 我们先把绳子剪掉第一段(长度为j),如果只剪掉长度为1,对最后的乘积无任何增益,所以从长度为2开始剪
    (4) 剪了第一段后,剩下(i - j)长度可以剪也可以不剪。如果不剪的话长度乘积即为j * (i - j);如果剪的话长度乘积即为j * dp[i - j]。取两者最大值max(j * (i - j), j * dp[i - j])
    (5) 第一段长度j可以取的区间为[2,i),对所有j不同的情况取最大值,因此最终dp[i]的转移方程为
    dp[i] = max (dp[i], max(j (i - j)*, j * dp[i - j]))
    (6) 最后返回dp[n]即可

题解:

#1)数学推导
class Solution:
	def cuttingRope(self, n: int):
		if n<=3:return n-1
		a, b = n//3, n%3
		if b == 0:return int(math.pow(3,a))
		if b == 1:return int(math.pow(3, a-1) * 4)
		return int(math.pow(3,a) * 2)

#2)贪心算法
class Solution:
	def cuttingRope(self, n: int):
		if n < 4:
			return n - 1
		res = 1
		while n > 4:
			res *= 3
			n -= 3
		return res * n
		
#3)动态规划
class Solution:
	def cuttingRope(self, n: int):	
		dp = [0] * (n+1)
		dp[2] = 1
		for i in range(3, n+1):
			for j in range(2, i):
				dp[i] = max(dp[i], max(j * (i-j), j * dp[i-j]))
		return dp[n]
  1. 在题2的基础上增加一个求余的操作
    唯一的不同之处在于,本题涉及 “大数越界的求余问题:大数越界: 剪绳子问题(不考虑取余)最终的结果是以3^a指数级别增长,可能超出int32 甚至 int64 的取值范围,导致返回值错误。
class Solution:
	def cuttingRope(self, n: int):	
		dp = [0] * (n+1)
		dp[2] = 1
		for i in range(3, n+1):
			for j in range(2, i):
				dp[i] = max(dp[i], max(j * (i-j), j * dp[i-j]))
		return dp[n] % int(1e9+7)

'''两层for循环。外层for循环实现绳子长度的动态变化,从4变到n,即首先计算出绳子长度为4的最大乘积,再依次往下进行;
内层for循环实现当前绳子长度i的动态分割,i//2 + 1的目的是绳子长度为5时,剪为2和3与3和2的乘积是一样的,避免重复计算;'''
class Solution(object):
    def cuttingRope(self, n):

    	if n == 2:
    		return 1
    	if n == 3:
    		return 2

    	dp = [0] * (n + 1)

    	#初始化
    	dp[0] = 0
    	dp[1] = 1
    	dp[2] = 2
    	dp[3] = 3

    	#两层循环的套路

    	for i in range(4,n + 1):


    		for j in range(0,i // 2 + 1):

    			dp[i] = max(dp[i], dp[j] * dp[i - j])

    	return dp[n] % 1000000007

疑问:为什么在1e9+7加int可以得到结果?这样写 int(dp[n] % (1e9+7))就会在120的时候出现错误?这是为什么呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值