198. 打家劫舍
1、第一次提交错了
class Solution:
def rob(self, nums: List[int]) -> int:
n = len(nums)
dp = [0]*n
dp[0]=nums[0]
dp[1]= max(nums[0],nums[1])
dp[2]=max(nums[0]+nums[2],nums[1])
for i in range(3,n):
dp[i] = max(dp[i-2]+nums[i-2],dp[i-3]+nums[i-3])
return dp[n-1]
2、在对dp【n】的定义上还需要明确。第一次定义dp【n】为偷完第n间房子的最大金额,应该改为偷到第n间房子为截至的最大金额;其次,在实际做的时候,dp【n】是还没有偷第n间房子的金额,与自己的定义不符。
3、dp【n】定义为截至到第n间房子为止能偷的最大金额。
首先,初始化0、1、2。
然后,写转移方程。要不从前2个过来;要不从前3个过来。根据dp定义,应该加上nums【n】
最后,从dp【n-2】和dp【n-1】中选最大值。
class Solution:
def rob(self, nums: List[int]) -> int:
n = len(nums)
dp = [0]*n
dp[0]=nums[0]
if n==1:
return dp[0]
dp[1]= max(nums[0],nums[1])
if n==2:
return dp[1]
dp[2]=max(nums[0]+nums[2],nums[1])
if n==3:
return dp[2]
for i in range(3,n):
dp[i] = max(dp[i-2]+nums[i],dp[i-3]+nums[i])
return max(dp[n-2],dp[n-1])
4、学习一下解析
高票答案是把dp【n】定义为偷完前n个房子的max money。【我稍作改动,更好理解一点】
转移方程是dp[n] = max(dp[n-1],dp[n-2]+nums[n]),即要不不偷这个房间,要不偷这个房间,选最大值。
我写的是可以从哪里偷过来,我觉得两种思路都是可以的。
我写的和高票答案都可以压缩空间,但是我写的没那么直观啦,需要4个变量,因为需要保存n、n-1、n-2、n-3;高票只需要3个。
213. 打家劫舍 II
1、好问题及好解答
原问题本身说的是首尾不能连接《》两个房子不能同时偷《》(两个房子都不偷+只偷其中一个)《==》划分成两个问题可以满足原条件
2、尼玛的,一直报错气死了
class Solution:
def rob(self, nums: List[int]) -> int:
N=len(nums)
def max_money(num):
n = len(num)
dp = [0]*n
dp[0]=num[0]
if n ==1:
return dp[0]
dp[1]=max(num[0],num[1])
if n==2:
return dp[1]
for i in range(2,n):
dp[i]=max(dp[i-1],dp[i-2]+num[i])
return max(dp[n-1],dp[n-2])
return max(max_money(nums[:N-1]),max_money(nums[1:]))
3、本来想写一个函数减少工作量,结果没考虑到返回值会出界的问题,需要先判断N==1。
class Solution:
def rob(self, nums: List[int]) -> int:
N=len(nums)
if N==1:
return nums[0]
def max_money(num):
n = len(num)
dp = [0]*n
dp[0]=num[0]
if n ==1:
return dp[0]
dp[1]=max(num[0],num[1])
if n==2:
return dp[1]
for i in range(2,n):
dp[i]=max(dp[i-1],dp[i-2]+num[i])
return max(dp[n-1],dp[n-2])
return max(max_money(nums[:N-1]),max_money(nums[1:]))
740. 删除并获得点数
1、一下子想不出来动态规划
2、牵扯到一个问题转换——将不规则排列,转换到规则的排列——用一个数组记录值为1,2,…,n的数量
3、
class Solution:
def deleteAndEarn(self, nums: List[int]) -> int:
N = max(nums)
if N==0:
return 0
listed_nums = [0]*(N)
for i in range(1,N+1):
listed_nums[i-1] = nums.count(i)*i
#问题转换为打家劫舍问题,不能取listed_nums中相邻的数值
dp = [0]*(N)
dp[0] = listed_nums[0]
if N==1:
return dp[0]
dp[1] = max(listed_nums[1],listed_nums[0])
if N==2:
return dp[1]
for i in range(2,N):
dp[i] = max(dp[i-1],dp[i-2]+listed_nums[i])
return max(dp[N-1],dp[N-2])
4、其实你刚开始已经意识到了,这个问题和打家劫舍问题很相似(不能取数值相邻的点),但是没有想到如何做数据处理,把原本不规则的数组,转换为不能取相邻值。具体转换方法就是,让数组中下标为i的地方储存原数组值为i+1的总和(储存值为0没有意义,所以从1开始储存;储存总和是因为一次直接加同一个数值的总和,所以储存一个数值的总和)。