一道经典的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. 我与微笑哥以及 Java 极客技术的前世今生

    ​关注公众号,大家可以在公众号后台回复“博客园”,免费获得作者 Java 知识体系/面试必看资料. Hello,大家好,我是子悠,Java 极客技术团队的作者之一,本周是六月的第三周,将由我给大家编辑 ...

  2. 警惕!CAF效应导致PCB漏电

    最近碰到一个PCB漏电的问题,起因是一款低功耗产品,本来整机uA级别的电流,常温老化使用了一段时间后发现其功耗上升,个别样机功耗甚至达到了mA级别.仔细排除了元器件问题,最终发现了一个5V电压点,在产 ...

  3. gradle脚本源码查看环境搭建

    背景 我刚入门学习gradle时,网上资料都是说通过gradle的api查看并学习脚本编写,但是api一般只有接口说明,并不能深入了解各个api的实现逻辑,有时就会对一些脚本的写法感到疑惑.通过搭建源 ...

  4. HTML/CSS:display:flex 布局教程

    网页布局(layout)是 CSS 的一个重点应用. 布局的传统解决方案,基于盒状模型,依赖 display 属性 + position属性 + float属性.它对于那些特殊布局非常不方便,比如,垂 ...

  5. Linux与Unix到底有什么不同?

    来自:开源中国 原文:Linux vs. Unix: What's the difference? 链接: https://opensource.com/article/18/5/difference ...

  6. net core Webapi基础工程搭建(一)——开发工具及环境

    目录 开发工具 版本 后端框架 开发工具 Visual Studio 2019,既然要折腾那就体验最新版的开发工具有什么特殊的地方,之前个人开发使用的是2017. 下载地址:https://visua ...

  7. 关于c++中的复合类型

    目录 数组 字符串 结构体 共用体 枚举 指针 数和指针的关系 常见的存储方式 数组替代品 一.数组 存储在每个元素中值的类型 数组名 数组中的元素数 通用格式:typename arrayname ...

  8. 彻底搞懂Java中equals和==的区别

    java当中的数据类型和“==”的含义: 1.基本数据类型(也称原始数据类型) :byte,short,char,int,long,float,double,boolean.他们之间的比较,应用双等号 ...

  9. OpenStack Telemetry系统架构及实践

    1. 概述 早期OpenStack的计量功能由Ceilometer项目负责,后来Ceilometer一分为四,每个项目负责一个方面的工作.不得不说这是OpenStack开发中的一个特色,比如Cinde ...

  10. 服务链路跟踪 && 服务监控

    服务链路跟踪 背景 微服务以微出名,在实际的开发过程中,涉及到成百上千个服务,网络请求引起服务之间的调用极其复杂. 当请求不可用或者变慢时,需要及时排查出故障服务点成为了微服务维护的一大难关. 服务链 ...