dp:能够找到规模更小与原问题相似的子问题
区间dp实际上是以区间为尺度的dp,一般具有以下特点:
- 可以将一个大区间的问题拆成若干个子问题合并的问题
- 两个连续的子区间可以进行整合,合并成一个大区间。
区间dp一般遵循以下方法:
- 从小到大枚举区间长度。
- 对于每一个区间,枚举所有可能出现的两个子区间的组合。
- 对于每一个区间,枚举所有可能出现的两个子区间的组合。
- 对于每一个区间的组合,计算出合并所需的代价。
- 求出当前区间的最优值,例如令
为区间
的最大价值,则f[i][j]为区间的最大价值,则
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类型题目总结:
可能性展开的常见方式:
- 基于两侧端点讨论的可能性展开
- 基于范围上划分点的可能性展开