区间动态规划

在这里插入图片描述

所谓区间dp,指在一段区间上进行动态规划,一般做法是由长度较小的区间往长度较大的区间进行递推,最终得到整个区间的答案,而边界就是长度为1以及2的区间。

转移方程

区间dp常见的转移方程如下:

dp(i,j) = min{dp(i,k-1) + dp(k,j)} + w(i,j)   (i < k <= j)
  1. d p ( i , j ) dp(i, j) dp(i,j) 表示处理从第 i i i 个元素到第 j j j 个元素(包括这两个元素)的子序列所需的最小代价或者最优值。这个代价或者最优值是由两部分组成的:一个是分割后的子区间的最优解之和,另一个是处理整个区间 [ i , j ] [i, j] [i,j] 本身的代价或者收益 w ( i , j ) w(i, j) w(i,j)

  2. m i n d p ( i , k − 1 ) + d p ( k , j ) min{dp(i, k-1) + dp(k, j)} mindp(i,k1)+dp(k,j) 这部分是核心,表示对于任意的分割点 k k k,你都将原始的区间 [ i , j ] [i, j] [i,j] 分割成两个小区间 [ i , k − 1 ] [i, k-1] [i,k1] [ k , j ] [k, j] [k,j],并对这两个小区间分别求解,然后取所有可能的分割方法中最优值的那一个。这个最优值可能来自任意一个分割点 k k k

  3. w ( i , j ) w(i, j) w(i,j) 是处理完整区间 [ i , j ] [i, j] [i,j] 的直接代价或者收益。这个代价或者收益通常取决于具体问题的设置,例如在一些字符串问题中, w ( i , j ) w(i, j) w(i,j) 可能代表将子字符串 [ i , j ] [i, j] [i,j] 转换成某种特定状态的代价。

  4. ( i < k < = j ) (i < k <= j) (i<k<=j) 确保了分割点 $k 总是位于区间 ( i , j ) (i, j) (i,j) 内部,确保了区间至少被分成两部分,防止了空区间的产生,这有助于避免无效的递归或者无意义的计算。

通过这样的方式,问题就被分解成了多个更小的问题,我们通过计算和比较所有可能的分割方法来找到最优解。这个过程通常通过两层循环(一层遍历所有可能的区间,另一层遍历所有可能的分割点)加上递归或者动态规划的方式来实现。

实例代码:

def calculate_w(i, j):
    # 这个函数应该根据问题的具体情况来定义
    # 它代表了处理从i到j这个区间的直接代价
    return 0  # 这里暂时假设为0,你需要根据实际情况进行修改

def interval_dp(n):
    # 初始化动态规划数组
    dp = [[float('inf')] * n for _ in range(n)]
    
    # 基本情况,通常是长度为1的区间
    for i in range(n):
        dp[i][i] = 0  # 或根据问题的具体情况进行初始化
    
    # 枚举区间长度len从2开始
    for len in range(2, n + 1):
        # 枚举区间起点i
        for i in range(n - len + 1):
            # 计算区间终点j
            j = i + len - 1
            # 枚举分割点k,并更新dp[i][j]
            for k in range(i + 1, j + 1):  # 注意k是从i+1开始的,确保区间至少被分成两部分
                dp[i][j] = min(dp[i][j], dp[i][k - 1] + dp[k][j] + calculate_w(i, j))
    
    # 返回整个序列的最优解
    return dp[0][n - 1]

len因为要把数组分成两个部分,而因为一边的长度最少为1,所以是从2开始。

例题

https://www.luogu.com.cn/problem/P1775

#include <bits/stdc++.h>
using namespace std;
int dp[1100][1100],p[1100],n,a[1100];
int sum(int l,int r){
	return p[r] - p[l-1];
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i = 1; i <= n; i++){
		cin>>a[i];
		p[i] = a[i]+p[i-1];
	}
	for(int i = 1; i <= n; i++) dp[i][i] = 0;
	for(int len = 2; len <= n+1; len++){
		for(int i = 1; i <= n; i++){
			int j = i+len-1;
			if(j > n) break;
			dp[i][j] = INT_MAX;
			for(int k = i+1; k <= j+1; k++){
				dp[i][j] = min(dp[i][j],dp[i][k-1]+dp[k][j]+sum(i,j));
			}
		}
	}
	cout<<dp[1][n]<<endl;
	return 0;
}
  • 23
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值