Acwing-基础算法课笔记之动态规划(区间DP)

Acwing-基础算法课笔记之动态规划(区间DP)

一、石子合并

1、定义

设有 N N N堆石子排成一排,其编号为 1 1 1 2 2 2 3 3 3,…, N N N
每堆石子有一定的质量,可以用一个整数来描述,现在要将这 N N N堆石子合并成为一堆。每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同。

例如:
例如有 4 4 4堆石子分别为 1 1 1 3 3 3 5 5 5 2 2 2, 我们可以先合并 1 1 1 2 2 2堆,代价为 4 4 4,得到 4 4 4 5 5 5 2 2 2, 又合并 1 1 1 2 2 2堆,代价为 9 9 9,得到 9 9 9 2 2 2,再合并得到 11 11 11,总代价为 4 + 9 + 11 = 24 4+9+11=24 4+9+11=24

如果第二步是先合并 2 2 2 3 3 3堆,则代价为 7 7 7,得到 4 4 4 7 7 7,最后一次合并代价为 11 11 11,总代价为 4 + 7 + 11 = 22 4+7+11=22 4+7+11=22

问题是:找出一种合理的方法,使总的代价最小,输出最小代价。

2、闫氏DP分析法

在这里插入图片描述

3、模拟过程

在这里插入图片描述
1、状态: f [ L , R ] f[L,R] f[L,R]表示把从 L L L R R R合并成一堆的最小代价。

利用前缀和求区间和

  1. 先把区间 [ L , R ] [L,R] [L,R]切分为两部分 [ L , K ] [L,K] [L,K] [ K + 1 , R ] [K+1,R] [K+1,R] K K K是切分点;
  2. 再把两部分 [ L , K ] [L,K] [L,K] [ K + 1 , R ] [K+1,R] [K+1,R]合并在一起。

状态转移方程如下:
f [ L , K ] + f [ K + 1 , R ] + s [ R ] − s [ L − 1 ] ⇒ f [ L , R ] f[L,K]+f[K+1,R]+s[R]-s[L-1]\Rarr f[L,R] f[L,K]+f[K+1,R]+s[R]s[L1]f[L,R]

2、计算:
f [ L , R ] = m i n ( f [ L , R ] , f [ L , K ] + f [ K + 1 , R ] + s [ R ] − s [ L − 1 ] ) f[L,R]=min(f[L,R],f[L,K]+f[K+1,R]+s[R]-s[L-1]) f[L,R]=min(f[L,R],f[L,K]+f[K+1,R]+s[R]s[L1])

3、初值:
f [ i , j ] = 0 f[i,j]=0 f[i,j]=0(合并每个石子的代价为 0 0 0),其余为正无穷

4、目标:
f [ 1 , n ] f[1,n] f[1,n]

4、代码示例

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 310;
int n;
int dp[N][N], s[N];
int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &s[i]);
		s[i] += s[i - 1];
	}
	for (int len = 2; len <= n; len++) {
		for (int l = 1; l + len - 1 <= n; l++) {//区间的移动
			int r = l + len - 1;
			dp[l][r] = 1e9;
			for (int k = l; k < r; k++) {
				dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r] + s[r] - s[l - 1]);
			}
		}
	}
	printf("%d", dp[1][n]);
	return 0;
}
  • 12
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,以下是一篇关于区间DP的学习笔记,希望对你有所帮助。 ### 什么是区间DP 区间 DP 是一种动态规划算法,用于解决一些区间上的问题。具体来说,区间 DP 通常用于解决如下问题: - 最长公共子序列(LCS) - 最长递增子序列(LIS) - 最大子段和 - 区间选数问题 区间 DP 通常采用分治或递推的方式进行求解,具体方法取决于问题的性质。 ### 区间 DP 的递推方法 区间 DP 的递推方法通常有两种,一种是自底向上的递推方法,一种是自顶向下的记忆化搜索方法。 自底向上的递推方法通常采用二维数组或三维数组来记录状态转移方程,具体的递推方式如下: ```cpp for (int len = 2; len <= n; len++) { for (int i = 1; i <= n - len + 1; i++) { int j = i + len - 1; for (int k = i; k < j; k++) { // 状态转移方程 } } } ``` 其中,len 表示区间长度,i 和 j 分别表示区间的左右端点,k 表示区间的划分点。 自顶向下的记忆化搜索方法通常采用记忆化数组来记录状态转移方程,具体的递推方式如下: ```cpp int dp(int i, int j) { if (i == j) return 0; if (memo[i][j] != -1) return memo[i][j]; memo[i][j] = INF; for (int k = i; k < j; k++) { memo[i][j] = min(memo[i][j], dp(i, k) + dp(k + 1, j) + ...); } return memo[i][j]; } ``` 其中,i 和 j 分别表示区间的左右端点,k 表示区间的划分点,memo 数组用于记忆化状态转移方程。 ### 区间 DP 的优化 对于一些区间 DP 问题,我们可以通过一些技巧和优化来减少时间和空间的消耗。 一种常见的优化方式是状态压缩,将二维或三维数组压缩成一维数组,从而减少空间的消耗。 另一种常见的优化方式是使用滚动数组,将数组的维度从二维或三维减少到一维,从而减少时间和空间的消耗。 此外,对于一些具有特殊性质的区间 DP 问题,我们还可以使用单调队列或单调栈等数据结构来进行优化,从而减少时间和空间的消耗。 ### 总结 区间 DP 是一种常用的动态规划算法,用于解决一些区间上的问题。区间 DP 通常采用分治或递推的方式进行求解,具体方法取决于问题的性质。对于一些区间 DP 问题,我们可以通过一些技巧和优化来减少时间和空间的消耗。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会敲代码的狗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值