DP之石子堆合并问题
(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之石子堆合并问题的更多相关文章
- dp入门 石子相邻合并 详细带图讲解
题目: 有N堆石子,现要将石子有序的合并成一堆,规定如下: 1.每次只能移动相邻的2堆石子合并 2.合并花费为新合成的一堆石子的数量. 求将这N堆石子合并成一堆的总花费最小(或最大). 样例: 输入 ...
- UESTC 886 方老师金币堆 --合并石子DP
环状合并石子问题. 环状无非是第n个要和第1个相邻.可以复制该行石子到原来那行的右边即可达到目的. 定义:dp[i][j]代表从第i堆合并至第j堆所要消耗的最小体力. 转移方程:dp[i][j]=mi ...
- 区间DP经典 石子合并
题目链接 题意:环形的一群石子,每次可以选择相邻的两堆合并,分数为新得到的一堆石子,求将这片石子合并成一堆的最大和最小分数 输入:第一行一个正整数n,其后n个数代表每堆石子的个数 分析:第一次写的时候 ...
- nyoj737区间dp(石子合并)
石子合并(一) 时间限制:1000 ms | 内存限制:65535 KB 难度:3 描述 有N堆石子排成一排,每堆石子有一定的数量.现要将N堆石子并成为一堆.合并的过程只能每次将相邻的 ...
- 区间dp之 "石子合并"系列(未完结)
A. 石子合并<1> 内存限制:128 MiB 时间限制:1000 ms 标准输入输出 题目类型:传统评测 方式:文本比较 题目描述 有N堆石子排成一排(n<=100),现要将石 ...
- 整数划分——区间dp(石子合并)
这不是将一个数以一来划分,而是把一个整数以位来划分 题目描述 如何把一个正整数N(N长度<20)划分为M(M>1)个部分,使这M个部分的乘积最大.N.M从键盘输入,输出最大值及一种划分方式 ...
- 2017北京网络赛 J Pangu and Stones 区间DP(石子归并)
#1636 : Pangu and Stones 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 In Chinese mythology, Pangu is the fi ...
- hihocoder1636 Pangu and Stones(区间DP(石子合并变形))
题目链接:http://hihocoder.com/problemset/problem/1636 题目大意:有n堆石头,每次只能合并l~r堆,每次合并的花费是要合并的石子的重量,问你合并n堆石子的最 ...
- dp——环形石子合并(区间dp)
环形的解决很巧妙 #include <iostream> #include <cstring> #include <string> #include <map ...
随机推荐
- 【vue】nextTick源码解析
1.整体入手 阅读代码和画画是一样的,忌讳一开始就从细节下手(比如一行一行读),我们先将细节代码折叠起来,整体观察nextTick源码的几大块. 折叠后代码如下图 整体观察代码结构 上图中,可以看到: ...
- python学习笔记(二)---for循环与操作列表
内容概要 for循环 range(start,end,step)函数 生成随机数列表 list()函数 将range()的结果整合到某个列表 列表的操作 切片(start: end :step) 元组 ...
- tp5 -- 控制器的参数
方法的参数是可以直接获取的到get和post这集中提交格式的数据的. 但是呢. 前置操作时不能这样操作的, 只能老老实实的使用input()这个方法来获取!!!
- kubeadm 默认镜像配置问题引申
背景: 每次使用功能kubeadm的时候都需要提前准备好镜像,为什么自定义使用的镜像源呢? 在没有翻越围墙时 kubeadm init --kubernetes-version=v1.13.0 --p ...
- Metasploit渗透测试环境搭建
渗透测试实验环境搭建 下载虚拟机镜像 5个虚拟机镜像,其中Linux攻击机我选择用最新的kali Linux镜像,其余的均使用本书配套的镜像. 网络环境配置 VMware虚拟网络编辑器配置: 将VMn ...
- IDEA设置导入主题样式皮肤,加入背景图片
主题下载地址:http://www.riaway.com/theme.php 里面有很多主题,看个人喜好,这里我用的Monokai Sublime Text 3. 导入主题打开IDEA,找到File ...
- jax-rs下载文件
@Path("/file") public class FileService { private static final String FILE_PATH = "c: ...
- 47000名开发者每月产生30000个漏洞 微软是如何用AI排查的
目前微软共有 47000 多名开发人员,每月会产生将近 30000 个漏洞,而这些漏洞会存储在 100 多个 AzureDevOps 和 GitHub 仓库中,以便于在被黑客利用之前快速发现关键的漏洞 ...
- (转)mysql数据库表名批量修改大小写
由于不用服务器对mysql的表名的大小写敏感要求不一致,经常在出现线上的数据库down到了本地不能运行的情况,贴出一段代码用来批量修改数据库表名大小写. DELIMITER // DROP PROCE ...
- 最小生成树之prime算法
public class Prime { public static void main(String[] args) { //路径矩阵 int arcs[][] = {{-1,6,1,5,-1,-1 ...