记录了初步解题思路 以及本地实现代码;并不一定为最优 也希望大家能一起探讨 一起进步
1.dp 动态规划
dp[i][value] 前i个数 答案为value的可能情况
因为-1000<=value<=1000
方便起见 全部加1000 => 0<=value<=2000
v为当前位置数值 j为当前考虑value值
dp[i][j] = dp[i-1][j-v] + dp[i-1][j+v]
2.dp 动态规划
总和为total 变负数的总和绝对值为neg 正数总和total-neg target=total-2*neg
转化为neg = (total-target)/2 选取若干个数 总和为neg
dp[i][j] 前i个数 总和可以为j的可能方式
num = nums[i]
dp[i+1][j] = dp[i][j]+dp[i][j-num]
def findTargetSumWays(nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
total = sum(nums)
if target>total or target<-total:
return 0
n = len(nums)
m = 2001
dp = [[0]*m for _ in range(n+1)]
dp[0][nums[0]+1000]+=1
dp[0][1000-nums[0]]+=1
for i in range(1,n):
v = nums[i]
for j in range(m):
if j-v>=0 and dp[i-1][j-v]>0:
dp[i][j] += dp[i-1][j-v]
if j+v<m and dp[i-1][j+v]>0:
dp[i][j]+=dp[i-1][j+v]
return dp[n-1][target+1000]
def findTargetSumWays2(nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
diff = sum(nums) - target
if diff<0 or diff%2==1:
return 0
n = len(nums)
neg = diff//2
dp = [[0]*(neg+1) for _ in range(n+1)]
dp[0][0]=1
for i in range(n):
num = nums[i]
for j in range(neg+1):
dp[i+1][j]=dp[i][j]
if j>=num:
dp[i+1][j] += dp[i][j-num]
return dp[n][neg]
将石子划分为两堆A,B sum(A)>=sum(B) 求两堆最小差值
total = sum(stones) sum(B) = neg
ans = total - 2 * neg
neg = (total-ans)/2
为了ans尽可能小 neg<=total/2 尽可能大
问题转化为 在stones中 选取若干个石子 尽可能大 并且小于等于total/2
dp[i][j] 表示前i个石子是否能凑出j的重量
dp[i+1][j] = dp[i][j] or dp[i][j-stones[i]]
def lastStoneWeightII(stones):
"""
:type stones: List[int]
:rtype: int
"""
total = sum(stones)
n = len(stones)
m = total//2
dp = [[False]*(m+1) for _ in range(n+1)]
dp[0][0]=True
for i in range(n):
for j in range(m+1):
if j<stones[i]:
dp[i+1][j] = dp[i][j]
else:
dp[i+1][j] = dp[i][j] or dp[i][j-stones[i]]
for j in range(m,-1,-1):
if dp[n][j]:
ans = total - 2*j
return ans
1.dp
三维dp[i][j][k]
前i个选项内 确定使用了j个人 至少得到k的收益
dp[0][0][0]=1
因为是至少 所以k<=minProfit
如果当前选项无法选取 有的人数少于需要的人数 j如果可以选取
dp[i][j][k] = dp[i-1][j][k]+dp[i-1][j-num][max(0,k-value)]
在max(0,k-value) 表示如果value>k 则前面即使是0也能够满足k的要求
2.dp
因为dp[i]只与dp[i-1]有关 可以取消这一维度
压缩成二维
dp[j][k]选取j个人得到利益k的情况
对于利润0 无论k为多少都能满足 dp[j][0]=1 此时j的含义可理解为最多使用j个人
逆序遍历 保证dp[j][k]使用的dp[j-num][max(0,k-value)]为上一个选项的情况
def profitableSchemes(n, minProfit, group, profit):
"""
:type n: int
:type minProfit: int
:type group: List[int]
:type profit: List[int]
:rtype: int
"""
mod=10**9+7
dp=[[[0]*(minProfit+1) for _ in range(n+1)] for _ in range(len(group)+1)]
dp[0][0][0]=1
for i in range(1,len(group)+1):
num,value = group[i-1],profit[i-1]
for j in range(n+1):
for k in range(minProfit+1):
if j<num:
dp[i][j][k] = dp[i-1][j][k]
else:
dp[i][j][k] = (dp[i-1][j][k]+dp[i-1][j-num][max(0,k-value)])%mod
ans = 0
for j in range(n+1):
ans = (ans + dp[len(group)][j][minProfit])%mod
return(ans)
def profitableSchemes2(n, minProfit, group, profit):
"""
:type n: int
:type minProfit: int
:type group: List[int]
:type profit: List[int]
:rtype: int
"""
mod=10**9+7
dp=[[0]*(minProfit+1) for _ in range(n+1)]
dp[0][0]=1
#for j in range(n+1):
# dp[j][0]=1
for i in range(1,len(group)+1):
num,value = group[i-1],profit[i-1]
for j in range(n,num-1,-1):
for k in range(minProfit,-1,-1):
dp[j][k] = (dp[j][k]+dp[j-num][max(0,k-value)])%mod
ans =0
for j in range(n+1):
ans += dp[j][minProfit]
return ans
完全背包问题
1.动态规划
前i个面额可以凑出j的方法
dp[i][j] = dp[i-1][j]+dp[i][j-coins[i-1]]
2.压缩 二维变一维
因为可以无限次数使用 所以j无需逆序考虑
def change(amount, coins):
"""
:type amount: int
:type coins: List[int]
:rtype: int
"""
n = len(coins)
dp = [[0]*(amount+1) for _ in range(n+1)]
for i in range(1,n+1):
dp[i][0] = 1
for i in range(1,n+1):
for j in range(1,amount+1):
if j - coins[i-1]>=0:
dp[i][j] = dp[i-1][j]+dp[i][j-coins[i-1]]
else:
dp[i][j] = dp[i-1][j]
return dp[n][amount]
def change2(amount, coins):
"""
:type amount: int
:type coins: List[int]
:rtype: int
"""
n = len(coins)
dp = [0]*(amount+1)
dp[0]=1
for i in range(1,n+1):
for j in range(coins[i-1],amount+1):
dp[j] += dp[j-coins[i-1]]
return dp[amount]
找到num^2<=n 可以生成可选列表[1^2, 2^2 , 3^2 … num^2]
1.动态规划
转换为完全背包问题
在可选列表中 选取最少的数 和为n
dp[j]=x 和为j是最少选取x个数
2.BFS
每次将结果减去所有可能性 记作一次操作
率先将结果变为0时 操作次数最少
3.DFS深度优先
遍历个数times的可能性 [1,n]
选取一个数 则将times-1
def numSquares(n):
"""
:type n: int
:rtype: int
"""
num = int(n**0.5)
l = [i**2 for i in range(num+1)]
dp =[float('inf')]*(n+1)
dp[0]=0
for i in range(1,len(l)+1):
v = l[i-1]
for j in range(v,n+1):
dp[j] = min(dp[j],dp[j-v]+1)
return dp[n]
def numSquares2(n):
"""
:type n: int
:rtype: int
"""
num = int(n**0.5)
l = [i**2 for i in range(num+1)]
queue = [(n,0)]
visited = set()
while queue:
tmp,times = queue.pop()
for v in l:
target = tmp-v
if target==0:
return times+1
if target not in visited:
queue.append((target,times+1))
visited.add(target)
return 0
def numSquares3(n):
"""
:type n: int
:rtype: int
"""
s = set([i**2 for i in range(int(n**0.5),0,-1)])
def dfs(n,times):
if times==1:
return n in s
for num in s:
if num>n:
continue
if dfs(n-num,times-1):
return True
return False
for i in range(1,n+1):
if dfs(n,i):
return i
return -1
若成本相同 取大的数
single记录不重复的cost
m记录每个cost能够取到的最大数字
1.尽量多选取个数使用dp dp[j]代表凑到j能够组成的最大值字符串
因为需要凑到target 所以初始化特殊字符""
dp[0]="" 表示可以添加
函数bigger用来比较两个字符串s1,s2的大小
函数insert用来将字符c插入到字符串s中 能够使得s最大
最后dp[target] 若为’'则表示无法得到 返回0
2.换一种dp目标 dp[j]代表和凑到j能够得到的最多字符个数
dp[target]为凑满target能够取到的最多字符个数
为了使相同个数的字符组成最大值字符串 需要将大的数字放在前面
从9遍历到1 v=cost[num] 如果dp[target]-1 == dp[target-v]
说明这个num被选中并且是最大的 将它加入结果中
def largestNumber(cost, target):
"""
:type cost: List[int]
:type target: int
:rtype: str
"""
single = list(set(cost))
m = {}
for i in range(9):
v = cost[i]
m[v] = str(i+1)
def bigger(s1,s2):
if len(s1)>len(s2):
return True
elif len(s1)<len(s2):
return False
else:
for i in range(len(s1)):
if s1[i]>s2[i]:
return True
elif s1[i]<s2[i]:
return False
return False
def insert(s,c):
if s=="":
return c
for i in range(len(s)):
if s[i]<c:
return s[:i]+c+s[i:]
return s+c
dp = ["*" for _ in range(target+1)]
dp[0] = ""
for i in range(1,len(single)+1):
v = single[i-1]
for j in range(v,target+1):
if dp[j-v]!="*":
tmp = insert(dp[j-v],m[v])
if bigger(tmp,dp[j]):
dp[j] = tmp
return dp[target] if dp[target]!="*" else "0"
def largestNumber2(cost, target):
"""
:type cost: List[int]
:type target: int
:rtype: str
"""
single = list(set(cost))
m = {}
for i in range(9):
v = cost[i]
m[v] = str(i+1)
dp = [float('-inf') for _ in range(target+1)]
dp[0]=0
for i in range(1,len(single)+1):
v = single[i-1]
for j in range(v,target+1):
dp[j] = max(dp[j],dp[j-v]+1)
if dp[target]==float('-inf'):
return "0"
ret = ""
pre = target
while pre>0:
for i in range(8,-1,-1):
v = cost[i]
if v>pre:
continue
if dp[pre-v]==dp[pre]-1:
ret +=str(i+1)
pre = pre-v
break
return ret if ret!="" else "0"
出现第一个错误版本的位置为ans
根据题目可知
[1,…,ans-1] 都为false
[ans,…,n]都为true
二分 找寻第一次出现true
def firstBadVersion(n):
"""
:type n: int
:rtype: int
"""
l,r = 1,n
while l<r:
mid = l+(r-l)//2
if isBadVersion(mid):
r = mid
else:
l = mid+1
return l