一道经典的dp题

在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.

我们先看下这道题的简单版本

有N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值。

这道题不是环状的,我们可以直接dp解决,一开始我设的是f[i][j]表是合并i-j这个区间内的最小代价,于是有了状态转移方程

f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]) (i<=k<=j)

于是写下了下面的代码

#include<bits/stdc++.h>
using namespace std;
int n,f[110][110],a[110];
int s[110];
int main(){
scanf("%d",&n);
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
f[i][i]=0;
s[i]=s[i-1]+a[i];
}
for(int i=1;i<=n;++i){
for(int j=i;j<=n;++j){
for(int k=i;k<=j;++k){
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
}
}
}
printf("%d",f[1][n]);
return 0;
}

然鹅答案错误,为什么?

在同机房的大佬的帮助下我明白了

因为求大区间是要用到小区间的值,可是上面这个程序固定了起点就一直向后跑会导致有些点更新过晚,要用的时候却用不到,于是我写下了下面的代码

#include<bits/stdc++.h>
using namespace std;
int n,f[110][110],a[110],T=10;
int s[110];
int main(){
scanf("%d",&n);
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
f[i][i]=0;
s[i]=s[i-1]+a[i];
}
while(T--){
for(int i=1;i<=n;++i){
for(int j=i;j<=n;++j){
for(int k=i;k<=j;++k){
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
}
}
}
}
printf("%d",f[1][n]);
return 0;
}

既然一遍跑不出答案,那我多跑几遍不就好了,同机房的大佬都震惊了,虽然答案是对的,但却并不是正解,正解应该是在外面枚举长度,再枚举起点,算出终点dp

代码

#include<bits/stdc++.h>
using namespace std;
int n,f[110][110],a[110],T=10;
int s[110];
int main(){
scanf("%d",&n);
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
f[i][i]=0;
s[i]=s[i-1]+a[i];
}
for(int L=2;L<=n;++L){
for(int i=1;i<=n;++i){
int j=i+L-1;
for(int k=i;k<=j;++k){
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
}
}
}
printf("%d",f[1][n]);
return 0;
}

回到 noi1995这道题,我们看到环便可直接加倍断环成链(套路),其余思路同上面相似

#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,f[410][410],g[410][410],a[410];
int s[410],maxn,minn=1<<30;
int main(){
scanf("%d",&n);
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
f[i][i]=0;
s[i]=s[i-1]+a[i];
}
for(int i=1;i<=n;++i){
s[i+n]=s[i+n-1]+a[i];
f[i+n][i+n]=0;//这个初始化一定要记得
}
for(int L=2;L<=n;++L){
for(int i=1;i<=n+n;++i){//起点可以枚举到2n
int j=i+L-1;
if(j>n*2) break;//不符合
for(int k=i;k<j;++k){
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
g[i][j]=max(g[i][j],g[i][k]+g[k+1][j]+s[j]-s[i-1]);
}
}
}
for(int i=1;i<=n;++i){
maxn=max(maxn,g[i][i+n-1]);
minn=min(minn,f[i][i+n-1]);
}
printf("%d\n%d\n",minn,maxn);
return 0;
}

[NOI1995]石子合并 题解的更多相关文章

  1. 洛谷 P1880 [NOI1995]石子合并 题解

    P1880 [NOI1995]石子合并 题目描述 在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分. 试 ...

  2. P1880 [NOI1995]石子合并[区间dp+四边形不等式优化]

    P1880 [NOI1995]石子合并 丢个地址就跑(关于四边形不等式复杂度是n方的证明) 嗯所以这题利用决策的单调性来减少k断点的枚举次数.具体看lyd书.这部分很生疏,但是我还是选择先不管了. # ...

  3. 区间DP小结 及例题分析:P1880 [NOI1995]石子合并,P1063 能量项链

    区间类动态规划 一.基本概念 区间类动态规划是线性动态规划的拓展,它在分阶段划分问题时,与阶段中元素出现的顺序和由前一阶段的那些元素合并而来由很大的关系.例如状态f [ i ][ j ],它表示以已合 ...

  4. P1880 [NOI1995]石子合并 区间dp

    P1880 [NOI1995]石子合并 #include <bits/stdc++.h> using namespace std; ; const int inf = 0x3f3f3f3f ...

  5. 【区间dp】- P1880 [NOI1995] 石子合并

    记录一下第一道ac的区间dp 题目:P1880 [NOI1995] 石子合并 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 代码: #include <iostream> ...

  6. 洛谷 P1880 [NOI1995] 石子合并(区间DP)

    传送门 https://www.cnblogs.com/violet-acmer/p/9852294.html 题解: 这道题是石子合并问题稍微升级版 这道题和经典石子合并问题的不同在于,经典的石子合 ...

  7. [洛谷P1880][NOI1995]石子合并

    区间DP模板题 区间DP模板Code: ;len<=n;len++) { ;i<=*n-;i++) //区间左端点 { ; //区间右端点 for(int k=i;k<j;k++) ...

  8. NOI1995石子合并&多种石子合并

    题目描述 在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分. 试设计出1个算法,计算出将N堆石子合并成1 ...

  9. 区间DP初探 P1880 [NOI1995]石子合并

    https://www.luogu.org/problemnew/show/P1880 区间dp,顾名思义,是以区间为阶段的一种线性dp的拓展 状态常定义为$f[i][j]$,表示区间[i,j]的某种 ...

随机推荐

  1. CountDownLatch实现多线程并发请求

    package com.test; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Dat ...

  2. js的位运算(其它语言也通用)

    左移运算符(<<) 该运算符有2个运算数,a<<b,将a左移相当于a乘以2的b次方,2个运算符要求是整数,或可以转换成整数的. 如:1<<2 =4 "1& ...

  3. Unity实现放大缩小以及相机位置平移实现拖拽效果

    放大缩小功能是游戏开发中用到的功能,今天就来讲一下Unity中放大缩小怎么实现. 1.IDragHandler, IBeginDragHandler, IEndDragHandler这三个接口是Uni ...

  4. Spark 系列(五)—— Spark 运行模式与作业提交

    一.作业提交 1.1 spark-submit Spark 所有模式均使用 spark-submit 命令提交作业,其格式如下: ./bin/spark-submit \ --class <ma ...

  5. quick-cocos2dx在eclipse下的lua调试

    文中大部分内容来自http://cn.quick-x.com/?p=253,绿色标记部分为修改部分. 配置编译环境的基本步骤: 安装 Visual Studio 2012 安装 Java SDK 安装 ...

  6. docker配置国内镜像地址

    docker的官方镜像站被大天朝强了,今天发现阿里有镜像加速这个功能,目前好像是在公测中,废话不多说,接下来告诉你怎么操作. 点击进入阿里镜像库 https://cr.console.aliyun.c ...

  7. python 之 前端开发(基本选择器、组合选择器、 交集与并集选择器、序列选择器、属性选择器、伪类选择器、伪元素选择器)

    11.3 css 11.31 基本选择器 11.311 id选择器 根据指定的id名称,在当前界面中找到对应的唯一一个的标签,然后设置属性 <!DOCTYPE html> <html ...

  8. Spark安装与部署

    1.首先安装scala(找到合适版本的具体地址下载) 在/usr/local/目录下 wget https://www.scala-lang.org/download/**** 2.安装spark ( ...

  9. 构建企业级数据湖?Azure Data Lake Storage Gen2不容错过(上)

    背景 相较传统的重量级OLAP数据仓库,“数据湖”以其数据体量大.综合成本低.支持非结构化数据.查询灵活多变等特点,受到越来越多企业的青睐,逐渐成为了现代数据平台的核心和架构范式. 数据湖的核心功能, ...

  10. size命令的sysv和berkeley格式差别

    size命令使用说明 size命令用于显示二进制文件的段(节)大小,其功能类似于readelf -S,详细的说明如下: 用法:size [选项] [文件] 显示二进制文件中节的大小 没有给出输入文件, ...