算法与设计分析作业2(动态规划)
1 Largest Divisible Subset
Given a set of distinct positive integers, find the largest subset such that every pair (Si,Sj) of elements in this subset satisfies:Si%Sj = 0 or Sj%Si = 0.
The optimal substructure and DP equation
首先将数组升序排序,这样我们就可以把较大的问题转换成较小的问题。dp[i]表示以dp[i]为最大数的符合要求子集的最大元素个数,max[i]表示到i位置为止符合要求子集的最大元素个数。因此该题可以将n个元素的符合要求的最大子集转变为前n-1个元素符合要求的最大子集(直至只剩下一个元素)。递推式为:
另外我们利用pre[]数组记录前一个可整除的值的下标,最后根据这些下标找到自己元素。
Pseudo-code
# @param {Integer[]} nums
# @return {Integer[]}
function largest_divisible_subset(nums)
if nums.length==0 then return [] end
nums数组升序排序
建立dp数组并填充1
建立max数组并填充1
建立pre数组并填充为自己的下标
for i in 1...nums.length
for j in 0...i
if nums[i]%nums[j]==0 && dp[i]<dp[j]+1
dp[i]=dp[j]+1
pre[i]=j
end
end
max[i]=max(dp[i],max[i-1])
end
建立子集subset
取max数组中第一个最大值的下标x
while pre[x]!=x
根据下标指示将元素nums[x]加入到子集subset中
x=pre[x]
end
将最后一个元素nums[x]加入到子集subset中
返回自己subset
end
Prove the correctness
首先该问题包含最优子结构的性质,我们可以将n个元素的符合要求的最大子集转变为前n-1个元素符合要求的最大子集(直至只剩下一个元素)。其次该问题包含重叠子性质,在解题过程中许多子问题需要重复计算,比如计算dp[3]是需要计算dp[2]+1,计算dp[4]需要计算dp[2]+1,那么dp[2]就是需要重复计算的,因此可以用”备忘录”将其记录。综上该题用动态规划是正确的。
The complexity of your algorithm
首先排序算法的复杂度为O(nlogn),求dp过程中的循环次数为n*(n-1)/2,复杂度为O(n2),最后回溯求子集的复杂度为O(n),因此最后的时间复杂度为O(n2)。
2 Money robbing
A robber is planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.
- Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.
- What if all houses are arranged in a circle?
The optimal substructure and DP equation
对于问题1,dp[i]表示从0~i家房子能抢到钱的最大数目,对于每一个房子,我们可以做一个决策,抢钱或者不抢钱。若是不抢钱,我们在该房子位置能抢到的最多钱就是前一个房子的能抢到的最多钱dp[i]=dp[i-1],若是抢钱,我们在该房子位置能抢到的最多钱就是当前房子能抢到钱加上前二个房子能抢到的最多钱dp[i]=dp[i-2]+nums[i]。两种决策的最大值就是该房子位置能抢到的最多钱。因此递推式为:
对于问题2,递推式是不变的,由于房子形成一个环,所以第一个房子和最后一个房子不能同时抢,所以只能抢第一个房子或者抢最后一个房子,因此我们只需要求0~nums.length-2号房子能抢到的最多钱和1~nums.length-1号房子能抢到的最多钱,两者比较求最大值。
Pseudo-code
# @param {Integer[]} nums
# @return {Integer}
# question 1:
function rob(nums)
if nums.length==0