(1)相邻:在一个圆形操场的四周摆放着n堆石子(n<= 100),现要将石子有次序地合并成一堆。规定每次只能选取相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。选择一种合并石子的方案,使得做n-1次合并,得分的总和最小。

 /*
* 在一个圆形操场的四周摆放着n堆石子(n<= 100),现要将石子有次序地合并成一堆。
* 规定每次只能选取相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
*选择一种合并石子的方案,使得做n-1次合并,得分的总和最小。
*/
public static void test(int[]a) {
//b[i][j]表示i-j相加之和,求出每一斜列的最小值就是这一步相加
int[][]b=new int[a.length][a.length];
//赋初值
for(int i=0;i<a.length;i++) {
b[i][i]=a[i];
System.out.print(a[i]+" ");
}
int sum=0;
System.out.println("score:"+sum);
for(int r=1;r<=a.length-1;r++) {
int min=b[1][r]+b[0][0];
int index1=0,index2=r;
b[0][r]=min;
for(int i=1;i<a.length-r;i++) {
int j=i+r;
b[i][j]=b[i+1][j]+b[i][i];
if(b[i][j]<min) {
min=b[i][j];
index1=i;
index2=j;
}
} for(int i=0;i<a.length;i++) {
if(i<index1||i>index2) {
System.out.print(a[i]+" ");
}else if(i==index1){//出现最小值
System.out.print(min+" ");
}
}
sum+=min;
System.out.println("score:"+sum);
}
}

思路就是类似矩阵连乘,关键就是最优解不好表示或者我写的有问题。。。(明天再来改!!!今天累了)Ok得出结论了就是我的想法有问题。

下面是标准DP:

设dp[i][j]表示第i到第j堆石子合并的最优值(合并i-j的花费的最小力气),sum[i][j]表示第i到第j堆石子的总数量。那么就有状态转移公式: 

因为每一个石子堆无非是和左边合并或者先和右边合并。对第i到第j的每一个k(子问题)计算他们的最小值加上每堆石子的总数量因为最后一步总是把所有的都合并。

我们可以从最小问题想起来:假如这个数组只有三个数,那么最小力气就是min(左边两个,右边两个)+三个的总量(最后一步)

有四个数时,将它们分为1-3,2-4两个子问题再继续分...

 public static void testA(int[]a) {
class Point{
int start;
int end;
public Point(int start, int end) {
this.start = start;
this.end = end;
}
}
//b[i][j]表示第i堆到第j堆的最优合并值
int[][]b=new int[a.length][a.length];
//s表示i到j的和
int[][]s=new int[a.length][a.length];
//保存最优解 0左 1下
int[][]l=new int[a.length][a.length];
Stack<Point> stack=new Stack<Point>();
//赋初值
for(int i=0;i<a.length;i++) {
b[i][i]=0;
s[i][i]=a[i];
} for(int r=1;r<=a.length-1;r++) {
for(int i=0;i<a.length-r;i++) {
int j=i+r;
s[i][j]=s[i][j-1]+s[j][j];
if(b[i][j-1]<b[i+1][j]) {//左边小
b[i][j]=b[i][j-1]+s[i][j];
l[i][j]=0;
}else {//下边小
b[i][j]=b[i+1][j]+s[i][j];
l[i][j]=1;
}
}
}
int x=0,y=a.length-1;
for(int i=0;i<a.length;i++) {
stack.add(new Point(x,y));
if(l[x][y]==0)//左边
y--;
else
x++;
} while(!stack.isEmpty()) {
Point p=stack.pop();
for(int i=0;i<a.length;i++) {
if(i<p.start||i>p.end)
System.out.print(a[i]+" ");
else if(i==p.start)
System.out.print(s[p.start][p.end]+" ");
}
System.out.println("score:"+b[p.start][p.end]);
}
}

(2)环形:就是不是相邻的是成环的

最简单的就是穷举相邻的找出最小的。这个时候把上面函数的返回值设为int然后计算。

第二种方法是将两边各扩展一位。因为只和左右相关边界位置应与头尾相连,然后进行相同的计算,最后在几种方案中挑出最小的。

 public static void testB(int[]a) {
class Point{
int start;
int end;
public Point(int start, int end) {
this.start = start;
this.end = end;
}
}
//首尾相连
int[]b=new int[a.length+2];
b[0]=a[a.length-1];
b[b.length-1]=a[0];
for(int i=0;i<a.length;i++) {
b[i+1]=a[i];
} //i-j的最优
int[][]d=new int[b.length][b.length];
//sum
int[][]s=new int[b.length][b.length];
//最优解0左 1下
int[][]l=new int[b.length][b.length];
Stack<Point> stack=new Stack<Point>();
//赋初值
for(int i=0;i<b.length;i++) {
d[i][i]=0;
s[i][i]=b[i];
}
//可以不填满,只填到需要的就行
int min=-1;
Point end=new Point(-1,-1);
for(int r=1;r<=a.length-1;r++) {
for(int i=0;i<b.length-r;i++) {
int j=i+r;
s[i][j]=s[i][j-1]+s[j][j];
if(d[i][j-1]<d[i+1][j]) {//左边
d[i][j]=d[i][j-1]+s[i][j];
l[i][j]=0;
}else {//下边
d[i][j]=d[i+1][j]+s[i][j];
l[i][j]=1;
}
if(r==a.length-1) {//找到最小的那个的值并记录解
if(min==-1)
{
min=d[i][j];
end.start=i;
end.end=j;
}
else if(min>d[i][j])
{
min=d[i][j];
end.start=i;
end.end=j;
}
}
}
}
int x=end.start,y=end.end;
for(int i=0;i<a.length;i++) {
stack.add(new Point(x,y));
if(l[x][y]==0)//左边
y--;
else
x++;
}
while(!stack.isEmpty()) {
Point p=stack.pop();
for(int i=0;i<a.length;i++) {
if(i<p.start||i>p.end)
System.out.print(b[i]+" ");
else if(i==p.start)
System.out.print(s[p.start][p.end]+" ");
}
System.out.println("score:"+d[p.start][p.end]);
}
}

(3)总结

这个是典型的DP问题,因为已经处理过之前的矩阵连乘问题,所以一开始我陷入了固定思维只想着填表,而没有去找子问题一步一步来。本末倒置。导致思路很不清晰但是代码似乎更简短。在处理环形时,选取了在两边扩展一位再每次分组计算最后取出一部分。

DP之石子堆合并问题的更多相关文章

  1. dp入门 石子相邻合并 详细带图讲解

    题目: 有N堆石子,现要将石子有序的合并成一堆,规定如下: 1.每次只能移动相邻的2堆石子合并  2.合并花费为新合成的一堆石子的数量. 求将这N堆石子合并成一堆的总花费最小(或最大). 样例: 输入 ...

  2. UESTC 886 方老师金币堆 --合并石子DP

    环状合并石子问题. 环状无非是第n个要和第1个相邻.可以复制该行石子到原来那行的右边即可达到目的. 定义:dp[i][j]代表从第i堆合并至第j堆所要消耗的最小体力. 转移方程:dp[i][j]=mi ...

  3. 区间DP经典 石子合并

    题目链接 题意:环形的一群石子,每次可以选择相邻的两堆合并,分数为新得到的一堆石子,求将这片石子合并成一堆的最大和最小分数 输入:第一行一个正整数n,其后n个数代表每堆石子的个数 分析:第一次写的时候 ...

  4. nyoj737区间dp(石子合并)

    石子合并(一) 时间限制:1000 ms  |  内存限制:65535 KB 难度:3   描述     有N堆石子排成一排,每堆石子有一定的数量.现要将N堆石子并成为一堆.合并的过程只能每次将相邻的 ...

  5. 区间dp之 "石子合并"系列(未完结)

    A. 石子合并<1> 内存限制:128 MiB 时间限制:1000 ms 标准输入输出 题目类型:传统评测 方式:文本比较   题目描述 有N堆石子排成一排(n<=100),现要将石 ...

  6. 整数划分——区间dp(石子合并)

    这不是将一个数以一来划分,而是把一个整数以位来划分 题目描述 如何把一个正整数N(N长度<20)划分为M(M>1)个部分,使这M个部分的乘积最大.N.M从键盘输入,输出最大值及一种划分方式 ...

  7. 2017北京网络赛 J Pangu and Stones 区间DP(石子归并)

    #1636 : Pangu and Stones 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 In Chinese mythology, Pangu is the fi ...

  8. hihocoder1636 Pangu and Stones(区间DP(石子合并变形))

    题目链接:http://hihocoder.com/problemset/problem/1636 题目大意:有n堆石头,每次只能合并l~r堆,每次合并的花费是要合并的石子的重量,问你合并n堆石子的最 ...

  9. dp——环形石子合并(区间dp)

    环形的解决很巧妙 #include <iostream> #include <cstring> #include <string> #include <map ...

随机推荐

  1. D - Leading and Trailing LightOJ - 1282

    题解:求n^k的前三位和后三位. 后三位直接快速幂对1000去余就可以了.前三位可以转换成浮点数来操作,也是用快速幂,我们只保留答案的前三位,当前值大于1000.0的话就除以10,直到结果小于等于10 ...

  2. c++全排列

    一.概念 从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列.当m=n时所有的排列情况叫全排列.如果这组数有n个,那么全排列数为n!个. 比如a ...

  3. 远程登录redis

    没想到吧,我居然已经摸到了redis. 远程登录redis redis-cli -h 127.0.0.1 -p 6379 ip地址和端口记得换成自己的

  4. pyinstaller打包

    参考 官网:http://www.pyinstaller.org/ pyinstaller参数使用 使用spec文件 安装 Windows依赖pypiwin32,新版的pyinstaller已经包含了 ...

  5. 2020-MRCTF

    ez_bypass I put something in F12 for you include 'flag.php'; $flag='MRCTF{xxxxxxxxxxxxxxxxxxxxxxxxx} ...

  6. Mybatis Generator通用Join的实现

    通常,我们使用Mybatis实现join表关联的时候,一般都是通过在xml或注解里写自定义sql实现. 本文通过Mybatis Generator的插件功能新增一个JoinPlugin插件,只要在配置 ...

  7. 异常处理的方式二:throws+异常类型

    package com.yhqtv.demo01Exception; import java.io.File; import java.io.FileInputStream; import java. ...

  8. 敏捷与OKR实践(如何让OKR与敏捷计划共存)

    僵化的详细长期计划(根据消耗的预算跟踪进度)正在敏捷组织中迅速成为对过去的褪色怀旧记忆,这由预测和非静态路线图代替.定期在这些可视化文件前聚会,您将能够学习.共享并触发重要的对话,解决依赖性并邀请服务 ...

  9. serialize和json_encode 区别

    (1)serialize主要用于php的序列化,存储到文件或者数据库中,json_encode 也是序列化,但是 主要用于与其他语言比如js进行交互使用,对于传输来说,json有许多优点. (2)在显 ...

  10. NGINX反向代理,后端服务器获取真实IP

    一般使用中间件做一个反向代理后,后端的web服务器是无法获取到真实的IP地址. 但是生产上,这又是不允许的,那么怎么解决? 1.在NGINX反向代理服务器上进行修改 2.修改后端web服务器配置文件 ...