dp 石子合并问题

£神魔★判官ぃ 2022-08-18 00:58 326阅读 0赞

石子合并问题:

有n(N<100)堆石子,价值分别为a0,a1……a(n-1),每次将其中的相邻的两堆合并,合并的代价为两堆石子的价值和,合并后用合并之后的一堆石子代替之前的两堆石子,价值为原来

两堆价值之和,求最终将所有的石子合并成一堆之后的代价最小值。

问题一:n堆石子排成一条直线

这个问题比较简单,类似与矩阵连乘的问题。

dp[n][m] 为合并n和m之间的石子的代价

则有dp[i][j] = 0 , i==j

dp[n][m] = min(dp[n][k]+dp[k+1][m]) + sum(n,m) n<=k<m;

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <string>
  5. #include <cmath>
  6. #include <algorithm>
  7. using namespace std;
  8. const int INF = 1<<20 ;
  9. const int maxn = 105 ;
  10. int sum[maxn];
  11. int dp[maxn][maxn];
  12. int n,a[maxn];
  13. int main()
  14. {
  15. while(scanf("%d",&n)!=EOF)
  16. {
  17. memset(sum,0,sizeof(sum));
  18. scanf("%d",&a[0]);
  19. sum[0]=a[0];
  20. for(int i = 1 ; i < n ; i++)
  21. {
  22. scanf("%d",&a[i]);
  23. sum[i] = sum[i-1] + a[i] ;
  24. }
  25. for(int i = 0 ; i < n ; i++)
  26. {
  27. dp[i][i] = 0 ;
  28. }
  29. for(int v = 1 ; v < n ; v++)
  30. {
  31. for(int i = 0 ; i < n-v ; i++)
  32. {
  33. int j = i+v ;
  34. int sumij = sum[j] - (i > 0 ? sum[i-1] : 0 ) ;
  35. dp[i][j] = INF ;
  36. for(int k = i ; k < j ; k++)
  37. {
  38. dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]+sumij);
  39. }
  40. }
  41. }
  42. printf("%d\n",dp[0][n-1]);
  43. }
  44. return 0;
  45. }
  46. //直线取石子的平行四边形的优化
  47. #include <iostream>
  48. #include <string.h>
  49. #include <stdio.h>
  50. using namespace std;
  51. const int INF = 1 << 30;
  52. const int N = 1005;
  53. int dp[N][N];
  54. int p[N][N];
  55. int sum[N];
  56. int n;
  57. int getMinval()
  58. {
  59. for(int i=1; i<=n; i++)
  60. {
  61. dp[i][i] = 0;
  62. p[i][i] = i;
  63. }
  64. for(int len=1; len<n; len++)
  65. {
  66. for(int i=1; i+len<=n; i++)
  67. {
  68. int end = i+len;
  69. int tmp = INF;
  70. int k = 0;
  71. for(int j=p[i][end-1]; j<=p[i+1][end]; j++)
  72. {
  73. if(dp[i][j] + dp[j+1][end] + sum[end] - sum[i-1] < tmp)
  74. {
  75. tmp = dp[i][j] + dp[j+1][end] + sum[end] - sum[i-1];
  76. k = j;
  77. }
  78. }
  79. dp[i][end] = tmp;
  80. p[i][end] = k;
  81. }
  82. }
  83. return dp[1][n];
  84. }
  85. int main()
  86. {
  87. while(scanf("%d",&n)!=EOF)
  88. {
  89. sum[0] = 0;
  90. for(int i=1; i<=n; i++)
  91. {
  92. int val;
  93. scanf("%d",&val);
  94. sum[i] = sum[i-1] + val;
  95. }
  96. printf("%d\n",getMinval());
  97. }
  98. return 0;
  99. }

问题二:n堆石子围成一圈

如果石子是排成圆形,其余条件不变,那么最优值又是什么呢?
因为圆形是首尾相接的,初一想,似乎与直线排列完全成了两个不同的问题。因为每次合并后我们都要考虑最后一个与第一个的合并关系。直线版的矩阵连乘对角线式的最优子结构不见了。f(i, j)表示i-j合并的最优值似乎并不可行,因为我们可以得到的最优值第一步就是第一个与最后一个合并,那么f(i, j)并不能表示这种关系。
修改一下,f(i, j)表示从第i个开始,合并后面j个得到的最优值。sum(i, j)表示从第i个开始直到i+j个的数量和。那么这个问题就得到解决了。注意要把其看成环形,即在有限域内的合并。

破圆化直:将圆形的石子归并化为直线型石子归并。
方法是:将原来的石子长度增加一倍,加在原来的后面,a[1]~a[n],a[1]~a[n],
求从1,2,3,~n开始的n个合并的最小值,最其中一个最小值即可。

![Image 1][]

  1. #include <iostream>
  2. #include <string.h>
  3. #include <stdio.h>
  4. using namespace std;
  5. const int INF = 1 << 30;
  6. const int N = 205;
  7. int mins[N][N];
  8. int maxs[N][N];
  9. int sum[N],a[N];
  10. int minval,maxval;
  11. int n;
  12. int getsum(int i,int j)
  13. {
  14. if(i+j >= n) return getsum(i,n-i-1) + getsum(0,(i+j)%n);
  15. else return sum[i+j] - (i>0 ? sum[i-1]:0);
  16. }
  17. void Work(int a[],int n)
  18. {
  19. for(int i=0;i<n;i++)
  20. mins[i][0] = maxs[i][0] = 0;
  21. for(int j=1;j<n;j++)
  22. {
  23. for(int i=0;i<n;i++)
  24. {
  25. mins[i][j] = INF;
  26. maxs[i][j] = 0;
  27. for(int k=0;k<j;k++)
  28. {
  29. mins[i][j] = min(mins[i][j],mins[i][k] + mins[(i+k+1)%n][j-k-1] + getsum(i,j));
  30. maxs[i][j] = max(maxs[i][j],maxs[i][k] + maxs[(i+k+1)%n][j-k-1] + getsum(i,j));
  31. }
  32. }
  33. }
  34. minval = mins[0][n-1];
  35. maxval = maxs[0][n-1];
  36. for(int i=0;i<n;i++)
  37. {
  38. minval = min(minval,mins[i][n-1]);
  39. maxval = max(maxval,maxs[i][n-1]);
  40. }
  41. }
  42. int main()
  43. {
  44. while(scanf("%d",&n)!=EOF)
  45. {
  46. for(int i=0;i<n;i++)
  47. scanf("%d",&a[i]);
  48. sum[0] = a[0];
  49. for(int i=1;i<n;i++)
  50. sum[i] = sum[i-1] + a[i];
  51. Work(a,n);
  52. printf("%d %d\n",minval,maxval);
  53. }
  54. return 0;
  55. }
  56. /*
  57. Auther:LIUYAN
  58. 2015.12.02
  59. 4 4 4 5 9
  60. 6 3 4 6 5 4 2
  61. */

[Image 1]:

发表评论

表情:
评论列表 (有 0 条评论,326人围观)

还没有评论,来说两句吧...

相关阅读

    相关 石子合并问题 (区间dp

    石子合并问题是最经典的DP问题。首先它有如下3种题型: (1)有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动任意的2堆石子合并,合并花费为新合成的

    相关 282 石子合并(区间dp

    1. 问题描述: 设有 N 堆石子排成一排,其编号为 1,2,3,…,N。每堆石子有一定的质量,可以用一个整数来描述,现在要将这 N 堆石子合并成为一堆。每次只能合并相邻的两

    相关 石子合并问题

    在一个圆形操场的四周摆放着n堆石子。现要将石子有次序地合并成一堆。规定每次只能选择相邻的两堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。试设计一个算法,计算出将n

    相关 ACM DP 石子合并问题

    滴,集训第二十一天打卡。 可能是对组队不太满意,都不大高兴做新的训练... 所以最近一直在磨DP,翻一下博客,发现最近都是DP啊... 这个石子合并的,我做的训练数据n是