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)。
输出格式
输出一个整数,表示最小代价。
数据范围
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 }