C++ 算法篇 动态规划----区间动态规划

区间动态规划的含义与模板解释

区间DP,其实求的就是一个区间内的最优值.
一般这种题目,在设置状态的时候,都可以设f[i][j]为区间i-j的最优值
而f[i][j]的最优值,这有两个小区间合并而来的,为了划分这两个更小的区间,我们则需用用一个循环变量k来枚举,而一般的状态转移方程便是:

f[i][j] = max/min (f[i][j] , f[i][k] + f[k][j] + something)   

我们则需要根据这个题目的实际含义进行变通即可.
而区间dp的大致模板是:

for (int len=2;len<=n;len++)
    for (int i=1;i+len-1<=n;i++)
    {   int j=i+len-1;
        for (int k=i;k<=j;k++)
            f[i][j]=max/min(f[i][j],f[i][k]+f[k][j]+something)
    }

len枚举区间的长度,i和j分别是区间的起点和终点,k的作用是用来划分区间.

 

例1、石子合并

题目描述

在一个圆形操场的四周摆放 N 堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出一个算法,计算出将 N 堆石子合并成 1 堆的最小得分和最大得分。

输入格式

数据的第 1 行是正整数 N,表示有 N 堆石子。

第 2 行有 N 个整数,第 i 个整数 ai​ 表示第 i 堆石子的个数。

输出格式

输出共 2 行,第 1行为最小得分,第 2 行为最大得分。

输入输出样例

输入

4
4 5 9 4

输出

43
54

说明/提示

1≤N≤100,0≤ai​≤20。

分析

一、确定算法

第一眼看见:最小得分与最大得分,便想到要用贪心或dp。贪心能用吗?不能,因为只有相邻两堆才能合并,这样就无法贪心啦!

所以,此题正解就是  区间dp

二、前置操作

当一条链来做, 最后一次合并一定是两堆石子合并,从第一个元素开始,这两堆石子有可能的是:

第1种可能性:(4),(5,9,4) 4是一堆石子,(5,9,4)合并的一堆石子  结果为:14+18+22 =54                

第2种可能性:(4,5),(9,4)    结果为:9+13+21=43

第3种可能性:(4,5,9),(4)    结果为:14+18+22=54

所以,我们更加抽象一点来思考,设dp(i,j)表示第i堆石子到第j堆石子合并成一堆石子的最大得分,a(i,j)表示第i到第j的和:

所以一条链的石子合并就OK了,关键环形是怎么做。

首先,发现要在一个首尾相连的环上dp并不方便,于是可以断环为链  ,

假设输入的数是a[1],a[2],……,a[n],那么使a[n+1]=a[1],a[n+2]=a[2],……,a[2*n]=a[n]  。

你会发现n个数的环上的每一段都在2*n个数的链上了 ,于是就在链上dp等价于在环上dp 。

三、动态规划三部曲:

1、状态

 设dp1[i][j]表示把从i到j的石子合并为一堆的最小得分,dp2[i][j]表示把从i到j的石子合并为一堆的最大得分(i<j)

2、转移方程

以最小值为例:

 假设存在k使得 将i到k合为一堆的最小得分(a堆)+将k+1到j合为一堆的最小得分(b堆)+将a堆与b堆合并的得分 < 将i到j合为一堆的最小得分 时,更新dp[i][j]  

 即dp1[i][j]=min(dp1[i][k]+dp1[k+1][j]+将a堆与b堆合并的得分,dp1[i][j])

所以现在唯一的未知量就是将a堆与b堆合并的得分了 。 

观察到,a堆的石子个数使a[i]+a[i+1]+……+a[k],b堆的石子个数是a[k+1]+a[k+2]+……+a[j]  , 所以将a堆与b堆合并的得分=a[i]+a[i+1]+…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值