LeetCode 每日一题 2021/6/7-2021/6/13

记录了初步解题思路 以及本地实现代码;并不一定为最优 也希望大家能一起探讨 一起进步


目录

      • 6/7 494. Target Sum 目标和
      • 6/8 1049. Last Stone Weight II 最后一块石头的重量 II
      • 6/9 879. Profitable Schemes 盈利计划
      • 6/10 518. Coin Change 2 零钱兑换 II
      • 6/11 279. Perfect Squares 完全平方数
      • 6/12 1449. Form Largest Integer With Digits That Add up to Target 数位成本和为目标值的最大数字
      • 6/13 278. First Bad Version 第一个错误的版本


6/7 494. Target Sum 目标和

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]

6/8 1049. Last Stone Weight II 最后一块石头的重量 II

将石子划分为两堆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

6/9 879. Profitable Schemes 盈利计划

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

6/10 518. Coin Change 2 零钱兑换 II

完全背包问题
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]

6/11 279. Perfect Squares 完全平方数

找到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

6/12 1449. Form Largest Integer With Digits That Add up to Target 数位成本和为目标值的最大数字

若成本相同 取大的数
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"

6/13 278. First Bad Version 第一个错误的版本

出现第一个错误版本的位置为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

你可能感兴趣的:(Exercise,算法,leetcode)