简单易懂的区间dp

dp:能够找到规模更小与原问题相似的子问题

区间dp实际上是以区间为尺度的dp,一般具有以下特点:

  • 可以将一个大区间的问题拆成若干个子问题合并的问题
  • 两个连续的子区间可以进行整合,合并成一个大区间。

区间dp一般遵循以下方法:

  • 从小到大枚举区间长度。
  • 对于每一个区间,枚举所有可能出现的两个子区间的组合。
  • 对于每一个区间,枚举所有可能出现的两个子区间的组合。
  • 对于每一个区间的组合,计算出合并所需的代价。
  • 求出当前区间的最优值,例如令 f[i][j] 为区间 [l,r] 的最大价值,则f[i][j]为区间的最大价值,则

f[i][j] = max{f[i][k] + f[k + 1][r] + cost | l <= k < r}

1233 石子合并

# 请在此输入您的代码
# 可以把一部分先合并,剩下的再合并。
# 令f[i][j]为区间[i,j]合成一堆的最小花费,那么可以枚举所有的中间点k将区间分为[i,k]和[k + 1,j]两部分。
# 在所有的当中取一个最小的。
# 转移方程dp[i][j] = dp[i][k]+dp[k+1][j]+sum(i, j)
import os
import sys
n=int(input())
stone=list(map(int,input().split()))
ans=[0]*(n+1)
for i in range(len(stone)):
  ans[i+1]=ans[i]+stone[i]              # 前缀和
dp=[[float('inf')]*n for _ in range(n)] # 求最小代价,初始值设为无穷大。
for i in range(n):
  dp[i][i]=0           # 每一堆石子本身代价为0
for l in range(2,n+1): # 枚举区间长度len,子区间长度(dp[i][k])一定小于该区间长度,len从小到大枚举保证每个子区间都被计算。
  for i in range(n):   # 枚举左端点
    j=i+l-1            # 计算右端点
    if j>=n:break
    for k in range(i,j):  # 枚举分界点,区间长度小说明之前就更新过了。
      dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+ans[j+1]-ans[i])
print(dp[0][-1])

926涂色:


RRRRR

RRRRRR

然后我再去涂B

RBGBG

以下标为1的为分割点

RB    GBG

RB = 涂两次

GBG = BG 涂两次

RBG    BG

RBG = R BG = R B G = 3

BG = B G = 2

import os
import sys

# 请在此输入您的代码
# 对于一个区间[l,r]考虑其端点处的颜色,只有端点颜色相同和不同两种情况。
s=input()
n=len(s)
dp=[[10**10]*n for _ in range(n)]#初始化极大值。
for i in range(n):
  dp[i][i]=1#每个单区间至少需要一次涂色。初始化。
#区间dp板子启动/
for lens in range(2,n+1):
  for i in range(n):
    j=i+lens-1
    #ij区间由i+1,j 和i,j-1转化而来
    if j>n-1:
      continue
    if s[i]==s[j]:#该区间左右端点相等。则第一次全涂涂到底,省下一次次数,直接继承。之后中间怎么解决,
    #那是中间的事情(对于中间而言中间和无色无区别)。
      dp[i][j]=min(dp[i+1][j],dp[i][j-1])
    else:#不相等,则一定是分开涂色的,隔板法
      for k in range(i,j): # 枚举k作为分界

        dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j])#

print(dp[0][-1])

1547制作回文串:

 一个字符串如果是回文串,再两边加上同一字母的话还是回文串

比如:ABCBA,DABCBAD‘ 那么就可以将问题缩小为[l+1,r-1]范围内

import os
import sys
# 请在此输入您的代码
# 如果一个串是回文串的话,在两边加同样字符仍然是回文串。
n , m = map(int,input().split())
s = input()

dic = {}
for _ in range(n):
    w = list(input().split())
    dic[w[0]] =min(int(w[1]),int(w[2]))

dp = [[0] * m for _ in range(m)] #dp[i][j]表示从i到j成为回文串的最小花费
for lens in range(2,m + 1):
    for i in range(m - lens + 1):
        j =lens + i - 1
        if s[i] == s[j]:
            dp[i][j] = dp[i + 1][j - 1]
        else:
            dp[i][j] = min((dp[i + 1][j] + dic[s[i]]),(dp[i][j - 1] + dic[s[j]]))
print(dp[0][m - 1])

区间dp类型题目总结:

可能性展开的常见方式:

  1. 基于两侧端点讨论的可能性展开
  2. 基于范围上划分点的可能性展开
  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值