leetcode-区间dp/数位dp

这篇博客探讨了几个使用动态规划和图论解决信息技术问题的案例,包括奇怪的打印机、回文子序列、戳气球和合并石头的最低成本问题。通过深入分析每个问题的解决方案,展示了这些算法在处理复杂计算问题时的有效性和灵活性。同时,博主还分享了代码实现,帮助读者理解如何在实际编程中应用这些概念。
摘要由CSDN通过智能技术生成

664. 奇怪的打印机

不能直接从前往后遍历。枚举

for k in range(i,j):
    dp[i][j] = min(min_val,dp[i][k]+dp[k+1][j])

的时候,k+1是大于i的,还没计算呢。

力扣

class Solution:
    def strangePrinter(self, s: str) -> int:
        #规律:s[i]==s[j]的时候,dp[i][j] = dp[i][j-1]
        #当s[i]!=s[j]时,没规律,枚举分割点找最小值
        dp = [[1 for _ in range(len(s))] for _ in range(len(s))]
        for i in range(len(s),-1,-1):
            for j in range(i+1,len(s)):
                if s[i] == s[j]:
                    dp[i][j] = dp[i][j-1]
                else:
                    min_val = float("inf")
                    for k in range(i,j):
                        dp[i][j] = min(min_val,dp[i][k]+dp[k+1][j])
                        min_val = dp[i][j]
        return dp[0][-1]

730. 统计不同回文子序列

 力扣

 

 

 今天这个每日一题,为什么去充的时候要left+1,right-1。因为区间如果是left,right的话,是不一定包含left,right这个字母的。

我们要找的是,包括left,right这个字母的。

class Solution:
    def countPalindromicSubsequences(self, s: str) -> int:
        dp = [[0 for _ in range(len(s)+1)] for _ in range(len(s)+1)]
        for i in range(1,len(s)+1):
            dp[i][i] = 1
        for i in range(1,len(s)+1):
            for j in range(i-1,0,-1):
            # for j in range(i):
                if s[i-1] == s[j-1]:
                    # if j-1 + 1 == i-1:
                    #     dp[j][i] = 2
                    #     continue
                    if s[i-1] not in s[j:i-1]:
                        dp[j][i] = 2*dp[j+1][i-1] + 2
                    elif s[i-1] in s[j:i-1]:
                        # #出现过一次;出现多2次及以上.这么写超时
                        # left = j
                        # right = j
                        # if_first = True
                        # count = 0
                        # for h in range(len(s[j:i-1])):
                        #     if s[j+h] == s[i-1] and if_first:
                        #         count += 1
                        #         #要找的是dp里的下标,是s的下标+1
                        #         left = j+h + 1
                        #         if_first = False
                        #     elif s[j+h] == s[i-1] and not if_first:
                        #         count += 1
                        #         right = j+h + 1

                        #出现过一次;出现多2次及以上
                        left = j 
                        while s[left] != s[i-1]:
                            left += 1
                        right = i-2
                        while s[right] != s[i-1]:
                            right -= 1 
                        if left == right:
                            dp[j][i] = 2*dp[j+1][i-1] + 1
                        else:
                            left += 1
                            right += 1
                            dp[j][i] = 2*dp[j+1][i-1] - dp[left+1][right-1]
                else:
                    dp[j][i] = dp[j+1][i]+dp[j][i-1] - dp[j+1][i-1]
                # dp[j][i] = dp[j][i] % (10**9+7)
        return dp[1][-1] % (10**9+7)

312. 戳气球

 力扣

class Solution:
    def maxCoins(self, nums: List[int]) -> int:

        #nums首尾添加1,方便处理边界情况
        nums.insert(0,1)
        nums.insert(len(nums),1)

        store = [[0]*(len(nums)) for i in range(len(nums))]

        def range_best(i,j):
            m = 0 
            #k是(i,j)区间内最后一个被戳的气球
            for k in range(i+1,j): #k取值在(i,j)开区间中
                #以下都是开区间(i,k), (k,j)
                left = store[i][k]
                right = store[k][j]
                a = left + nums[i]*nums[k]*nums[j] + right
                if a > m:
                    m = a
            store[i][j] = m

        #对每一个区间长度进行循环
        for n in range(2,len(nums)): #区间长度 #长度从3开始,n从2开始
            #开区间长度会从3一直到len(nums)
            #因为这里取的是range,所以最后一个数字是len(nums)-1

            #对于每一个区间长度,循环区间开头的i
            for i in range(0,len(nums)-n): #i+n = len(nums)-1

                #计算这个区间的最多金币
                range_best(i,i+n)

        return store[0][len(nums)-1]


作者:xiao-yan-gou
链接:https://leetcode.cn/problems/burst-balloons/solution/zhe-ge-cai-pu-zi-ji-zai-jia-ye-neng-zuo-guan-jian-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

1000. 合并石头的最低成本

 力扣

class Solution:
    def mergeStones(self, stones: List[int], k: int) -> int:
        if (len(stones) - k) % (k - 1) != 0:
            return -1
        n = len(stones)
        preSum = [0]
        for i in range(n):
            preSum.append(preSum[-1] + stones[i])
        
        @lru_cache(None)
        def dfs(l, r) -> int:  # (左右闭区间)把[l,r]合并成最少堆需要至少多少cost
            if r - l + 1 < k:  # base case::无法再合并
                return 0
            return min(
                dfs(l, i) + dfs(i + 1, r) + ((preSum[r + 1] - preSum[l]) if (r - l) % (k - 1) == 0 else 0) for i in
                range(l, r, k - 1))
        
        return dfs(0, n - 1)

作者:imnanch7
链接:https://leetcode.cn/problems/minimum-cost-to-merge-stones/solution/python3-qu-jian-xing-ji-yi-hua-dfs-mei-j-qn02/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值