[NOI1995]石子合并 题解
一道经典的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]石子合并 题解的更多相关文章
- 洛谷 P1880 [NOI1995]石子合并 题解
P1880 [NOI1995]石子合并 题目描述 在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分. 试 ...
- P1880 [NOI1995]石子合并[区间dp+四边形不等式优化]
P1880 [NOI1995]石子合并 丢个地址就跑(关于四边形不等式复杂度是n方的证明) 嗯所以这题利用决策的单调性来减少k断点的枚举次数.具体看lyd书.这部分很生疏,但是我还是选择先不管了. # ...
- 区间DP小结 及例题分析:P1880 [NOI1995]石子合并,P1063 能量项链
区间类动态规划 一.基本概念 区间类动态规划是线性动态规划的拓展,它在分阶段划分问题时,与阶段中元素出现的顺序和由前一阶段的那些元素合并而来由很大的关系.例如状态f [ i ][ j ],它表示以已合 ...
- P1880 [NOI1995]石子合并 区间dp
P1880 [NOI1995]石子合并 #include <bits/stdc++.h> using namespace std; ; const int inf = 0x3f3f3f3f ...
- 【区间dp】- P1880 [NOI1995] 石子合并
记录一下第一道ac的区间dp 题目:P1880 [NOI1995] 石子合并 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 代码: #include <iostream> ...
- 洛谷 P1880 [NOI1995] 石子合并(区间DP)
传送门 https://www.cnblogs.com/violet-acmer/p/9852294.html 题解: 这道题是石子合并问题稍微升级版 这道题和经典石子合并问题的不同在于,经典的石子合 ...
- [洛谷P1880][NOI1995]石子合并
区间DP模板题 区间DP模板Code: ;len<=n;len++) { ;i<=*n-;i++) //区间左端点 { ; //区间右端点 for(int k=i;k<j;k++) ...
- NOI1995石子合并&多种石子合并
题目描述 在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分. 试设计出1个算法,计算出将N堆石子合并成1 ...
- 区间DP初探 P1880 [NOI1995]石子合并
https://www.luogu.org/problemnew/show/P1880 区间dp,顾名思义,是以区间为阶段的一种线性dp的拓展 状态常定义为$f[i][j]$,表示区间[i,j]的某种 ...
随机推荐
- 关于dfs的套路
void dfs(答案, 搜索层数, 其他参数) { if (层数==maxdeep) { 更新答案 return; } (剪枝) for(下一层可能的状态){ 更新全局变量表示的状态的变量 dfs( ...
- SQL Labs刷题补坑记录(less31-less53)
LESS31: 双引号直接报错,那么肯定可以报错注入,并且也过滤了一些东西,^异或没有过滤,异或真香 -1" and (if(length(database())=8,1,0)) and & ...
- 自定义markdown代码高亮显示-cnblog
这个代码高亮..一点儿都不高亮...... cnblog里已经有闻道先者贴出代码了, https://www.cnblogs.com/liutongqing/p/7745413.html 效果大概是这 ...
- Logback配置文件这么写,TPS提高10倍
通过阅读本篇文章将了解到 1.日志输出到文件并根据LEVEL级别将日志分类保存到不同文件 2.通过异步输出日志减少磁盘IO提高性能 3.异步输出日志的原理 配置文件logback-spring.xml ...
- 【linux】【qt5界面】【系统托盘图标的实现】
前言: 博主最近在做一个聊天软件,虽然技术不咋滴,但遇到点干货肯定是要跟大家分享的啦.下面就给大家分享一个qt实现程序隐藏才系统托盘的技巧. 装备: 系统:linux, qt版本:5.9.2,GCC: ...
- selenium中的setUp,tearDown与setUpClass,tearDownClass的区别
def setUpClass(cls): cls.driver = webdriver.Chrome() cls.driver.maximize_window() def setUp(self): s ...
- 《深入理解Java虚拟机》- Java虚拟机是如何加载Java类的?
Java虚拟机是如何加载Java类的? 这个问题也就是面试常问到的Java类加载机制.在年初面试百战之后,菜鸟喜鹊也是能把这流程倒背如流啊!但是,也只是字面上的背诵,根本就是像上学时背书考试一样. ...
- Socket通信封装MIna框架--含羞代放
目录 核心类 各个击破 IoService IoFilter IoHandler 总结 # 加入战队 微信公众号 Mina异步IO使用的Java底层JNI框架,Mina提供服务端和客户端,将我们的业务 ...
- C#高级语法之泛型、泛型约束,类型安全、逆变和协变(思想原理)
一.为什么使用泛型? 泛型其实就是一个不确定的类型,可以用在类和方法上,泛型在声明期间没有明确的定义类型,编译完成之后会生成一个占位符,只有在调用者调用时,传入指定的类型,才会用确切的类型将占位符替换 ...
- idea(java)实用开发插件
Idea常用的插件: mybatisX, ---------------- (Alt + enter) codeGlace, Lombok, sonarlint, translation, ...