Acwing 282.石子合并 —— 区间dp

区间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)。

输出格式

输出一个整数,表示最小代价。

数据范围

1N3001≤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 }

 

posted @ 2020-11-12 02:21  筱翼深凉  阅读(195)  评论(0编辑  收藏  举报