区间dp.

一、定义:

区间dp是这样的一种动规:定义 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示区间 [ i , j ] [i,j] [i,j] 中最优解。用区间的长度 l e n len len 实现状态的转移。意思是说,用小区间的解转移到大区间的解,最终变成全局最最优解。

模版如下:

for(int l = 1;l <= n; ++l) // 长度
	for(int i = 1;i <= n - l + 1; ++i) { // 左端点
		int j = i + l - 1; // 算出右端点
		// 状态转移
	}

二、例题

  1. 石子合并

    设有 N ( N ≤ 300 ) N(N \le 300) N(N300) 堆石子排成一排,其编号为 1 , 2 , 3 , ⋯   , N 1,2,3,\cdots,N 1,2,3,,N。每堆石子有一定的质量 m i ( m i ≤ 1000 ) m_i(m_i \le 1000) mi(mi1000)。现在要将这 N N N 堆石子合并成为一堆。每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻。合并时由于选择的顺序不同,合并的总代价也不相同。试找出一种合理的方法,使总的代价最小,并输出最小代价。

分析:由于是合并操作,则这个区间是从小到大的,可以使用区间dp。定义 d p i , j dp_{i,j} dpi,j 为区间 [ i , j ] [i,j] [i,j] 经过合并后的最小代价。

每一次合并,都必然会产生 ∑ p = i j m p \sum_ {p=i}^j m_p p=ijmp 的代价(可用前缀和维护) 以及 左右两堆已有的代价和(即 d p i , k + d p k + 1 , j dp_{i,k}+dp_{k+1,j} dpi,k+dpk+1,j),我们只需要枚举 k k k

时间复杂度为 O ( n 3 ) O(n^3) O(n3)

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int Maxn = 305;
int n, a[Maxn], dp[Maxn][Maxn], pre[Maxn];
int main() {
	scanf("%d", &n);
	for(int i = 1;i <= n; ++i) scanf("%d", &a[i]);
	for(int i = 1;i <= n; ++i) pre[i] = pre[i - 1] + a[i];
	for(int l = 2;l <= n; ++l) 
		for(int i = 1;i <= n - l + 1; ++i) {
			int j = i + l - 1;
			dp[i][j] = 0x3f3f3f3f;
			for(int k = i;k < j; ++k) dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + pre[j] - pre[i - 1]); 
		}
	printf("%d", dp[1][n]);	
	return 0;
}

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值