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 ...
随机推荐
- overload 与override的区别
Override 是重写: 方法名称.参数个数,类型,顺序,返回值类型都是必须和父类方法一致的.它的关系是父子关系Overload 是重载: 方法名称不变,其余的都是可以变更的.它的关系是同一个类 ...
- 什么是CDN内容分发网络?【刘新宇】
CDN 使用第三方OSS服务的好处是集成了CDN服务,下面来了解一下什么是CDN. CDN 全称:Content Delivery Network或Content Distribute Network ...
- web前端该怎么入门?web前端入门教程(非常详细)
初学编程的小伙伴经常会遇到的问题,1.没资源 2.没人带 3.不知道从何开始 ,小编也是从新手期过来的,所以很能理解萌新的难处,现在整理一些以前自己学习的一些资料送给大家,希望对广大初学小伙伴有帮助! ...
- bootstrop设置背景图片自适应屏幕
如果不用bootstrop框架,想让背景图片自适应窗口大小,可以这样做: <style type="text/css"> html{height: %;} body.a ...
- weblogic补丁升级详细步骤,18.7.17补丁更新
weblogic打补丁 到weblogic官网下载补丁包 对应的补丁包 如: p22248372_1036012_Generic.zip 一 安装补丁步骤 1.登录linux的weblogic用户 ...
- VirtualBox 原始镜像转换成 vdi 镜像
VBoxManage convertdd [-static] <filename> <outputfile> 将raw硬盘转换成vdi虚拟硬盘
- (c++ std) 查找 vector 中的元素
You can use std::find from <algorithm>: std::find(vector.begin(), vector.end(), item) != vecto ...
- zookeeper笔记(一)
title: zookeeper笔记(一) zookeeper 安装简记 解压文件 $ tar -zxvf zookeeper-3.4.10.tar.gz -C 安装目录 创建软连接(进入安装目录) ...
- C/C++ 程序执行时间
C/C++中的计时函数是clock(),而与其相关的数据类型是clock_t.在MSDN中,查得对clock函数定义如下: clock_t clock( void ); 这个函数返回从“开启这个程序进 ...
- 阿里大牛带你深入分析spring事务传播行为
spring框架封装了很多有用的功能和组件,便于在项目开发中快速高效的调用,其中spring的事务使用非常简单,只需要在用到事务的地方加一行注解即可: 1@Transactional 但越是看起来简单 ...