动态规划———区间dp

区间dp:

首先引出例题:NOI 1995 石子合并

题目描述

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

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

输入输出格式

输入格式: 数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.

输出格式:输出共2行,第1行为最小得分,第2行为最大得分.

输入样例:                                                 输出样例:

4                                                                 43

4 5 9 4                                                        54

乍一看,还以为是贪心。当然有一些贪心思路,但都不是正解。这里正解为区间dp:

dpmax[i][j]表示区间内合并石子的 最大值, 然后我们就可以枚举 断点 k 。因为此题为圆形操场,环状上枚举断点。既然这样,我们就需要将这一圈石子分割。很显然,我们需要枚举一个k,来作为这一圈石子的分割线。

因为只能合并 相邻 的石子,也就是分值只和连续几堆石子数量的和有关,很容易想到用 前缀和 来实现: sum[i] 表示前 i 堆石子数量和。

由于可以头尾合并,于是我们就要多开 n 个空间,也要多循环 n 次。然后枚举 区间头端 ,一个个区间地找 最大值/最小值 ,最后输出即可。

于是我们得到动态转移方程:

f[i][j] = max(f[i][k] + f[k+1][j] + sum(i,j));其中,1<=i<=<=k

那么接下来就是考虑递推过程了:

我们需要枚举k来划分i和j,那么如果通过枚举i和j进行状态转移,很显然某些k值时并不能保证已经确定过所需状态。

但这样i和j就难以确定了。所以我们需要枚举j-i,并在j-i中枚举k。——————摘自TIMI_k的方法、

AC代码如下:

#include
#include
#include
#include 
using namespace std;
const int maxn=1e10;
int n,ans2,ans1,dp1[1001][1001],dp2[1001][1001],num[1001];
int s[1001];//连缀和数组 
inline int sum(int i,int j){return s[j]-s[i-1];}//分数为当前石头数量即为连缀和之差 
int main(){+
	cin>>n;
	for(int i=1;i<=n+n;i++){
		cin>>num[i];
		num[i+n]=num[i];
		s[i]=s[i-1]+num[i];//求连缀和过程Q 
	}
	for(int p=1;p



你可能感兴趣的:(动态规划———区间DP:)