当前位置:   article > 正文

区间dp_区间dp用的思想

区间dp用的思想

一、定义

区间DP,顾名思义是在区间上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 l:=1 to n do // l是区间长度,作为阶段。 
for i:=1 to n do // i是穷举的区间的起点
begin
j:=i+l-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; 
这个结构必须记好,这是区间动态规划的代码结构。
 

伪代码:

  1. //mst(dp,0) 初始化DP数组
  2. for(int i=1;i<=n;i++)
  3. {
  4. dp[i][i]=初始值
  5. }
  6. for(int len=2;len<=n;len++) //区间长度
  7. for(int i=1;i<=n;i++) //枚举起点
  8. {
  9. int j=i+len-1; //区间终点
  10. if(j>n) break; //越界结束
  11. for(int k=i;k<j;k++) //枚举分割点,构造状态转移方程
  12. {
  13. dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+w[i][j]);
  14. }
  15. }

三,经典例题

 

                                                            石子合并

 

描述 

有N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值。

输入

有多组测试数据,输入到文件结束。
每组测试数据第一行有一个整数n,表示有n堆石子。
接下来的一行有n(0< n <200)个数,分别表示这n堆石子的数目,用空格隔开

输出

输出总代价的最小值,占单独的一行

样例输入

3

1 2 3

7

13 7 8 16 21 4 18

样例输出

9

239

要求n个石子归并,我们根据dp的思想划分成子问题,先求出每两个合并的最小代价,然后每三个的最小代价,依次知道n个。

定义状态dp [ i ] [ j ]为从第i个石子到第j个石子的合并最小代价。

那么dp [ i ] [ j ] = min(dp [ i ] [ k ] + dp [ k+1 ] [ j ]) 

那么我们就可以从小到大依次枚举让石子合并,直到所有的石子都合并。

这个问题可以用到平行四边形优化,用一个s【i】【j】=k 表示区间 i---j 从k点分开才是最优的,这样的话我们就可以优化掉一层复杂度,变为O(n^2).

代码如下:

  1. #include <cstdio>
  2. #include <queue>
  3. #include <cstring>
  4. #include <algorithm>
  5. using namespace std;
  6. #define mst(a,b) memset((a),(b),sizeof(a))
  7. #define rush() int T;scanf("%d",&T);while(T--)
  8. typedef long long ll;
  9. const int maxn = 205;
  10. const ll mod = 1e9+7;
  11. const ll INF = 1e18;
  12. const double eps = 1e-9;
  13. int n,x;
  14. int sum[maxn];
  15. int dp[maxn][maxn];
  16. int main()
  17. {
  18. while(~scanf("%d",&n))
  19. {
  20. sum[0]=0;
  21. mst(dp,0x3f);
  22. for(int i=1;i<=n;i++)
  23. {
  24. scanf("%d",&x);
  25. sum[i]=sum[i-1]+x;
  26. dp[i][i]=0;
  27. }
  28. for(int len=2;len<=n;len++)
  29. for(int i=1;i<=n;i++)
  30. {
  31. int j=i+len-1;
  32. if(j>n) continue;
  33. for(int k=i;k<j;k++)
  34. {
  35. dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
  36. }
  37. }
  38. printf("%d\n",dp[1][n]);
  39. }
  40. return 0;
  41. }

【平行四边形优化】

上面的代码运行时间在240ms左右,通过这题完全没问题,但我们还可以考虑优化。

由于状态转移时是三重循环的,我们想能否把其中一层优化呢?尤其是枚举分割点的那个,显然我们用了大量的时间去寻找这个最优分割点,所以我们考虑把这个点找到后保存下来

用s[i][j]表示区间[i,j]中的最优分割点,那么第三重循环可以从[i,j-1)优化到【s[i][j-1],s[i+1][j]】。(这个时候小区间s[i][j-1]和s[i+1][j]的值已经求出来了,然后通过这个循环又可以得到s[i][j]的值)。


代码如下:

  1. #include <cstdio>
  2. #include <queue>
  3. #include <cstring>
  4. #include <algorithm>
  5. using namespace std;
  6. #define mst(a,b) memset((a),(b),sizeof(a))
  7. #define rush() int T;scanf("%d",&T);while(T--)
  8. typedef long long ll;
  9. const int maxn = 205;
  10. const ll mod = 1e9+7;
  11. const ll INF = 1e18;
  12. const double eps = 1e-9;
  13. int n,x;
  14. int sum[maxn];
  15. int dp[maxn][maxn];
  16. int s[maxn][maxn];
  17. int main()
  18. {
  19. while(~scanf("%d",&n))
  20. {
  21. sum[0]=0;
  22. mst(dp,0x3f);
  23. for(int i=1;i<=n;i++)
  24. {
  25. scanf("%d",&x);
  26. sum[i]=sum[i-1]+x;
  27. dp[i][i]=0;
  28. s[i][i]=i;
  29. }
  30. for(int len=2;len<=n;len++)
  31. for(int i=1;i<=n;i++)
  32. {
  33. int j=i+len-1;
  34. if(j>n) continue;
  35. for(int k=s[i][j-1];k<=s[i+1][j];k++)
  36. {
  37. if(dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]<dp[i][j])
  38. {
  39. dp[i][j]=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
  40. s[i][j]=k;
  41. }
  42. }
  43. }
  44. printf("%d\n",dp[1][n]);
  45. }
  46. return 0;
  47. }

时间【32ms】

 

以下是一些参考资料

石子归并,括号匹配,整数划分

https://blog.csdn.net/y990041769/article/details/24194605

 

石子合并(三) 环形合并

https://blog.csdn.net/qq_36183935/article/details/70808356

 

四边形不等式优化_石子合并问题_C++

https://www.cnblogs.com/hadilo/p/5800306.html

动态规划加速原理之四边形不等式

https://wenku.baidu.com/view/c44cd84733687e21af45a906.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/498815
推荐阅读
相关标签
  

闽ICP备14008679号