石子合并问题是最经典的 DP 问题,其有如下3种题型:

【任意合并】

1.问题:

有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动任意的2堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费最小(或最大)。

2.分析:

最简单的情况,合并的是任意两堆,直接贪心即可,每次选择最小的两堆合并,实际上就是哈夫曼树的变形。

3.实现:

int a[N];
int Compare(const void *pleft, const void *pright) {int *left = (int *)pleft;int *right = (int *)pright;return (*left - *right);
}
int main() {int n;while(scanf("%d", &n)!=EOF&&n) {for(int i = 0; i < n; i++)scanf("%d", &a[i]);for(int i = 0; i < n; i++) {qsort(&a[i], n - i, sizeof(a[0]), Compare);for(int j = 0; j < n; j++)printf("%d ", a[j]);printf("\n");a[i+1] += a[i]; //此时a[i]和a[i+1]就是最小的两个}printf("%d\n", a[n-1]);}
}

【直线下的相邻合并】

1.问题:

有N堆石子直线排列,现要将石子有序的合并成一堆,规定如下:每次只能移动相邻的2堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费最小(或最大)。

2.分析:

我们熟悉矩阵连乘,知道矩阵连乘也是每次合并相邻的两个矩阵,那么石子合并可以用矩阵连乘的方式来解决。

设 dp[i][j] 表示第 i 到第 j 堆石子合并的最优值,sum[i][j] 表示第 i 到第 j 堆石子的总数量。

那么就有状态转移方程:

3.实现

int dp[N][N];
int sum[N];
int a[N];
int getMinval(int a[],int n)
{  for(int i=0;i<n;i++)  dp[i][i] = 0;  for(int v=1;v<n;v++)  {  for(int i=0;i<n-v;i++)  {  int j = i + v;  dp[i][j] = INF;  int tmp = sum[j] - (i > 0 ? sum[i-1]:0);  for(int k=i;k<j;k++)  dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j] + tmp);  }  }  return dp[0][n-1];
}
int main()
{  int n;  while(scanf("%d",&n)!=EOF)  {  for(int i=0;i<n;i++)  scanf("%d",&a[i]);  sum[0] = a[0];  for(int i=1;i<n;i++)  sum[i] = sum[i-1] + a[i];  printf("%d\n",getMinval(a,n));  }  return 0;
}  

平行四边形优化

int dp[N][N];
int p[N][N];
int sum[N];
int n;
int getMinval()
{  for(int i=1; i<=n; i++)  {  dp[i][i] = 0;  p[i][i] = i;  }  for(int len=1; len<n; len++)  {  for(int i=1; i+len<=n; i++)  {  int end = i+len;  int tmp = INF;  int k = 0;  for(int j=p[i][end-1]; j<=p[i+1][end]; j++)  {  if(dp[i][j] + dp[j+1][end] + sum[end] - sum[i-1] < tmp)  {  tmp = dp[i][j] + dp[j+1][end] + sum[end] - sum[i-1];  k = j;  }  }  dp[i][end] = tmp;  p[i][end] = k;  }  }  return dp[1][n];
}
int main()
{  while(scanf("%d",&n)!=EOF)  {  sum[0] = 0;  for(int i=1; i<=n; i++)  {  int val;  scanf("%d",&val);  sum[i] = sum[i-1] + val;  }  printf("%d\n",getMinval());  }  return 0;
}  

【环形下的相邻合并】

1.问题

有N堆石子环形排列,现要将石子有序的合并成一堆,规定如下:每次只能移动相邻的2堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费最小(或最大)。

2.分析:

状态转移方程:

其中:

3.实现:

int mins[N][N];
int maxs[N][N];
int sum[N],a[N];
int minval,maxval;
int n;
int getsum(int i,int j)
{  if(i+j >= n) return getsum(i,n-i-1) + getsum(0,(i+j)%n);  else return sum[i+j] - (i>0 ? sum[i-1]:0);
}
void Work(int a[],int n)
{  for(int i=0;i<n;i++)  mins[i][0] = maxs[i][0] = 0;  for(int j=1;j<n;j++)  {  for(int i=0;i<n;i++)  {  mins[i][j] = INF;  maxs[i][j] = 0;  for(int k=0;k<j;k++)  {  mins[i][j] = min(mins[i][j],mins[i][k] + mins[(i+k+1)%n][j-k-1] + getsum(i,j));  maxs[i][j] = max(maxs[i][j],maxs[i][k] + maxs[(i+k+1)%n][j-k-1] + getsum(i,j));  }  }  }  minval = mins[0][n-1];  maxval = maxs[0][n-1];  for(int i=0;i<n;i++)  {  minval = min(minval,mins[i][n-1]);  maxval = max(maxval,maxs[i][n-1]);  }
}  int main()
{  while(scanf("%d",&n)!=EOF)  {  for(int i=0;i<n;i++)  scanf("%d",&a[i]);  sum[0] = a[0];  for(int i=1;i<n;i++)  sum[i] = sum[i-1] + a[i];  Work(a,n);  printf("%d %d\n",minval,maxval);  }  return 0;
} 

动态规划 —— 区间 DP —— 石子合并三讲相关推荐

  1. [动态规划] 区间DP

    区间DP 石子合并问题 题目链接 https://www.acwing.com/problem/content/description/284/ 状态表示和状态转移 f [ i ] [ j ] f[i ...

  2. 动态规划 —— 区间 DP

    [概述] 区间型动态规划,又称为合并类动态规划,是线性动态规划的扩展,它在分阶段地划分问题时,与阶段中元素出现的顺序和由前一阶段的区间中哪些元素合并而来有很大的关系. [思想] 区间 DP 实质上就是 ...

  3. 0x53. 动态规划 - 区间DP(习题详解 × 8)

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 文章目录 0x53. 动态规划 - 区间DP Problem A. 最优矩阵链乘 Problem B. ...

  4. 动态规划——区间dp

    在利用动态规划解决的一些实际问题当中,一类是基于区间上进行的,总的来说,这种区间dp是属于线性dp的一种.但是我们为了更好的分类,这里仍将其单独拿出进行分析讨论. 让我们结合一个题目开始对区间dp的探 ...

  5. 【动态规划笔记】区间dp:合并果子

      初始化:一堆果子不需要合并,所以dp[i][i]=0 既然之前说过我们需要枚举k来划分i和j,那么如果通过枚举i和j进行状态转移,很显然某些k值时并不能保证已经确定过所需状态. 如,i=1 to ...

  6. 【区间dp】合并沙子

    题目描述 设有N堆沙子排成一排,其编号为1,2,3,-,N(N< =300).每堆沙子有一定的数量,可以用一个整数来描述,现在要将这N堆沙子合并成为一堆,每次只能合并相邻的两堆,合并的代价为这两 ...

  7. Exploring Pyramids【动态规划——区间DP】

    Exploring Pyramids UVALive - 3516 题目传送门 题目大意:给你一个字符串,其代表的是机器人来回走过的路径,机器人总是先走左边再走右边,问有多少种情况. 解决方法:设输入 ...

  8. 【动态规划区间dp】蓝桥2019:最优包含

    字符串 二维dp: dp[ i ][ j ]表示S中前i个字符包含T中前j个字符至少修改S中前i个字符的字符数 如果S[ i ]==T[ j ] ,则不用修改第i个字符,dp[ i ][ j ] = ...

  9. dp2:线性dp、区间dp、计数dp.

    线性dp   动态规划时间复杂度分析,状态数目与状态转移次数相乘. 数字三角形 数字三角形 以集合的观点考虑dp问题. #include<iostream> #include<cst ...

最新文章

  1. 深证信息等三方拟联合开展大数据研究
  2. 111.什么是基带信号?什么是宽带信号?
  3. 源代码编译安装Apache2
  4. Tiny6410之重定位代码到SDRAM
  5. java开发简介_Java Web开发介绍
  6. 【算法竞赛学习】资金流入流出预测-挑战Baseline_建模预测
  7. bigdecimal判断等于0_shell 脚本中if判断的条件总结
  8. Springboot 使用校验框架validation校验
  9. javaScript 计算两个日期的天数相差~~~
  10. mysql56数据库的创建_如何在Mysql下用命令创建数据库用户方法
  11. 2012.4.17内存相关(二)
  12. JAVA排序:快速排序算法
  13. 磨皮ps教程-庞姿姿
  14. 在CSDN年收入竟达五位数?----大学生技术自媒体成长之路
  15. 由浅入深:自己动手开发模板引擎——解释型模板引擎(一)
  16. Qt: 读取/写入文本文件内容
  17. 状态栏和导航栏重叠,解决办法
  18. 移动端 实现图片上传
  19. 晶振保存和使用中的注意事项
  20. 2023,创客永不停歇

热门文章

  1. Google搜索正在“死亡”
  2. 终于有人把SaaS讲明白了
  3. S5PV210的内存映射
  4. python大一基础题_python基础练习题
  5. Java 18 新功能介绍
  6. 分享 2 个“捷径”,帮你 6 个月达到阿里 P7 水平
  7. 牛X,试用了下GitHub上22万Star的第一抢票神器,3秒钟抢到!
  8. 蚂蚁集团涵畅:再启程,Service Mesh 前路虽长,尤可期许
  9. 【JEECG技术文档】JEECG在线聊天插件功能集成文档
  10. Jeewx-Enterprise_1.1版本发布,开源微信企业号开发平台