dp[i][j]:i,j分别表示字符串的首尾索引
我们知道,大区间的结果总是依赖于小区间,由此
#初始化
dp[n-1][n-1]=True
for i in range(n-1):
dp[i][i]=True #一个字符肯定是回文
dp[i][i+1]=s[i]==s[i+1] #两个字符是否是回文
for i in range(n):
for j in range(i+2,n):
dp[i][j]=dp[i+1][j-1] and s[i]==s[j]
但是dp[i][j]依赖dp[i+1],必须先计算dp[i+1]才能计算dp[i],但是i的for循环又是从小到大的,所以此写法是有问题的。
for i in range(n-1,-1,-1):
for j in range(i+2,n):
dp[i][j]=dp[i+1][j-1] and s[i]==s[j]
我们将i倒过来遍历就ok了
优化
我们先循环长度,再循环起点
长的区间依赖于短的区间
我们先从长度3开始循环到n
for length in range(3,n+1):
for i in range(n-length+1):
j=length+i-1
dp[i][j]=dp[i+1][j-1] and s[i]==s[j]
1,石子归并
动规四要素
定义:dp[i][j] 表示下标i合并到j的最小耗费
状态转移方程:dp[i][j]=min(dp[i][k]+dp[k+1][j]+sum(i,j)) (k=0时,为dp[i][j])
初始化:dp[i][i]=0 因为一个数不能合并,花费为0
结果:dp[0][n-1]
n=len(a)
if n<2:
return 0
range_sum=[[0]*n for i in range(n)]
for i in range(n):
range_sum[i][i]=a[i]
for i in range(n):
for j in range(i+1,n):
range_sum[i][j]=range_sum[i][j-1]+a[j]
dp=[[float('inf')]*n for i in range(n)]
for i in range(n):
dp[i][i]=0
for length in range(2,n+1):
for i in range(n-length+1):
j=length+i-1
for k in range(i,j):
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+range_sum[i][j])
return dp[0][n-1]
注意:length的for循环起点要根据初始化而定,已经初始化的就不需要遍历。
2,吹气球
nums=[1,*nums,1]
n=len(nums)
dp=[[0]*n for i in range(n)]
for i in range(n):
dp[i][i]=nums[i]
for length in range(2,n+1):
for i in range(n-length+1):
j=length+i-1
for k in range(i+1,j):
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k][j]+nums[i]*nums[k]*nums[j])
return dp[0][n-1]
小结:
区间动态规划,小结题目中有 subarray / substring 的信息,大区间依赖小区间,循环方式不同。