区间dp问题,其基本思路就是
对于每段区间,他们的最优值都是由几段更小区间的最优值得到,是分治思想的一种应用,将一个区间问题不断划分为更小的区间直至一个元素组成的区间,枚举他们的组合 ,求合并后的最优值。
设F[i,j](1<=i<=j<=n)表示区间[i,j]内的数字相加的最小代价
最小区间F[i,i]=0(一个数字无法合并,∴代价为0)
每次用变量k(i<=k<=j-1)将区间分为[i,k]和[k+1,j]两段。
For len=1 to n do // len是区间长度,作为阶段。
for i=1 to n do // i是穷举的区间的起点
begin
j=i+len-1; // j是 区间的终点,这样所有的区间就穷举完毕
if j>n then break; // 这个if很关键。
for k:= i to j-1 do // 状态转移,去推出 f[ i , j ]
f[ i , j ]= max{f[ i , k ]+ f[ k+1 , j ]+ w[ i , j ] }
end;
这个结构必须记好,这是区间动态规划的代码结构。
设有N堆石子排成一排,其编号为1,2,3,…,N。
每堆石子有一定的质量,可以用一个整数来描述,现在要将这N堆石子合并成为一堆。
每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同。
例如有4堆石子分别为 1 3 5 2, 我们可以先合并1、2堆,代价为4,得到4 5 2, 又合并 1,2堆,代价为9,得到9 2 ,再合并得到11,总代价为4+9+11=24;
如果第二步是先合并2,3堆,则代价为7,得到4 7,最后一次合并代价为11,总代价为4+7+11=22。
问题是:找出一种合理的方法,使总的代价最小,输出最小代价。
输入格式
第一行一个数N表示石子的堆数N。
第二行N个数,表示每堆石子的质量(均不超过1000)。
输出格式
输出一个整数,表示最小代价。
数据范围
1≤N≤3001≤N≤300
输入样例:
4
1 3 5 2
输出样例:
22
对于这儿道题就是dp[ i ][ j ]表示从第i堆石子到第j堆石子的集合,而其属性就是合并相邻两堆石子花费的最小值。
其状态转移方程为dp[ i ][ j ]=min(dp[ i ][ j ],dp[ i ][ k ]+dp[ k+1 ][ r ]+s[ r ]-s[ l-1 ])。
1 #include<algorithm>
2 #include<cstring>
3 #include<iostream>
4
5 using namespace std;
6
7 const int N=330;
8 int s[N];
9 int n;
10 int dp[N][N];
11
12 int main()
13 {
14 memset(dp,0x3f,sizeof dp);
15 scanf("%d",&n);
16
17 for(int i=1;i<=n;i++)
18 {
19 cin>>s[i];
20 s[i]+=s[i-1];
21 dp[i][i]=0;
22 }
23
24 for(int len=2;len<=n;len++)
25 {
26 for(int i=1;i+len-1<=n;i++)
27 {
28 int l=i,r=i+len-1;
29 for(int k=l;k<r;k++)
30 dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]+s[r]-s[l-1]);
31 }
32 }
33
34 cout<<dp[1][n]<<endl;
35
36 return 0;
37 }