题目描述

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

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

输入输出格式

输入格式:

数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.

输出格式:

输出共2行,第1行为最小得分,第2行为最大得分.


1995的NOI题目,然而却是一道非常水的区间DP。

区间DP,顾名思义,求区间最值问题。通过小区间来更新大区间,最后逐渐更新出答案。

区间DP常用枚举套路:

for(int len = ;len<=n;len++)
{
for(int i = ;i+len-<=n;i++)
{
int j = i+len-;
dp[i][j] = ...
}
}

外层枚举长度,下一层枚举初始端点,终点通过长度+起点-1枚举出来,需要注意的是起点枚举范围是i+len-1,也就是终点要在区间长度以内。

继续说这道题目。

大区间一定是通过小区间合并出来的,这也是我们使用区间DP的原因。但是这道题并不是一条链上的石子,而是一个环。也就是说我们有可能在最后一个石子回头,与前面的石子合并。

那我们只需要把原来的链的长度变成二倍(除了最后一位),而枚举长度仍然是一倍的长度不就好了?

举个例子:

  2,3,4,5

我们可以把它变成2,3,4,5,2,3,4

枚举的时候仍然是4的长度。这样就完美的处理了链的情况。

接下来是状态转移方程。

不知道有没有同学会和我开始时候有一样的错觉,全部合并到一起不就是所有的值相加吗?

然而并不是这样的,当一个区间与另一个区间合并时,原来的区间的数被算了两次。

再举个栗子。

[1,2] 与[2,3]合并

前者合并之后是3,后者是5

这样在合并一次就是3+5+8

相当于1+2+2+3+1+2+2+3

虽然这个结论是错的,但是我们从中可以得到一个结论,每次合并的时候,区间内的所有值都会被再次算一遍。

所以要预处理前缀和。

接下来说方程。

dp[i][j]表示把[i][j]中的石子合并成一堆所需要的费用。

[i][j]之间我们可以选择任意一个点,把这个区间分成两段,通过这两段合并成这一段区间。

所以

dpmax[i][j] = max(dpmax[i][j],dpmax[i][k-]+dpmax[k][j]+before[j]-before[i-]);

dpmin[i][j] = min(dpmin[i][j],dpmin[i][k-]+dpmin[k][j]+before[j]-before[i-]);

before代表前缀和

每个点的初始值就是它自己的花费。

最后上代码。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
int num[];
int before[];
int dpmax[][];
int dpmin[][];
int main()
{
int n;
scanf("%d",&n);
for(int i = ;i<=n;i++)
{
scanf("%d",&num[i]);
num[n+i] = num[i];
before[i] = before[i-]+num[i];
}
for(int i = n+;i<=*n-;i++)
{
before[i] = before[i-]+num[i];
}
memset(dpmin,0x3f,sizeof(dpmin));
memset(dpmax,-,sizeof(dpmax));
for(int i = ;i<=*n-;i++)
{
dpmin[i][i] = ;
dpmax[i][i] = ;
}
for(int len = ;len<=n;len++)
{
for(int i = ;i+len-<=*n-;i++)
{
int j = i+len-;
for(int k = i+;k<=j;k++)
{
dpmax[i][j] = max(dpmax[i][j],dpmax[i][k-]+dpmax[k][j]+before[j]-before[i-]);
dpmin[i][j] = min(dpmin[i][j],dpmin[i][k-]+dpmin[k][j]+before[j]-before[i-]);
}
}
}
int ma = -;
int mi = ;
for(int i = ;i<=n;i++)
{
ma = max(ma,dpmax[i][i+n-]);
mi = min(mi,dpmin[i][i+n-]);
}
printf("%d\n%d",mi,ma);
return ;
}
/*
3
1 2 3
*/

石子合并(NOI1995)题解的更多相关文章

  1. [NOI1995]石子合并 题解

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

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

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

  3. 石子合并(NOI1995)

    石子合并(NOI1995) 时间限制: 1 Sec  内存限制: 128 MB提交: 90  解决: 48[提交][状态][讨论版] 题目描述 在操场上沿一直线排列着 n堆石子.现要将石子有次序地合并 ...

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. 【题解】Luogu P5288 [HNOI2019]多边形

    原题传送门 HN的题目就是毒瘤 我们有以下猜想: 1.最后所有的线都连到了n号点上 2.最小步数应该为n-3-已经连到n号点的线段数量 本来有些边\((a_i,n)\)会将整个图分割成很多个区间.对于 ...

  2. Linux文件比对,批量复制

    --背景 工作中突然有一天文件服务器空间满了,导致文件存不进去,立马换了另外一台服务器作为文件服务器,将服务器挂载上去,原来的服务器修复之后需要重新换回来,但是需要将临时使用的服务器内的文件迁移至原文 ...

  3. C# vb .net实现淡色效果滤镜

    在.net中,如何简单快捷地实现Photoshop滤镜组中的淡色效果呢?答案是调用SharpImage!专业图像特效滤镜和合成类库.下面开始演示关键代码,您也可以在文末下载全部源码: 设置授权 第一步 ...

  4. ASP.NET SignalR 系列(三)之代码实现

    说在前头: 因SignalR默认采用camel的编码规范,故前端调用后端的对象或者方法时,首字母均需要小写 创建集线器 创建完,文件中默认创建了一个不带参数Hello方法的示例,我们修改一下,带个参数 ...

  5. yum -y install java-1.8.0-openjdk-devel.x86_64

    yum -y install java-1.8.0-openjdk-devel.x86_64

  6. 【面试突击】- 2019年125条常见的java面试笔试题汇总(二)

    26.什么时候用assert. assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制.在实现中,assertion就是在程序中的一条语句,它对一个boolean表达 ...

  7. CSS样式表及选择器相关内容(一)

    CSS(Cascading Style Sheets)层叠样式表1.CSS级联-CSS层叠:规定在哪个HTML中使用哪个样式. body{ font-size:16px; //body默认字体大小 } ...

  8. 遍历js中的数组

    可以使用js中的for循环,或者forEach方法:也可以使用Ext中的方法遍历js中的数组 代码如下: /** * 遍历数组 */ var arr = ['越南', '新加坡', '美国', '俄罗 ...

  9. 使用 Create-React-App 开发 Chrome 扩展

    整理 Kindle 标注.书签和笔记从未如此简单! Kindle 标注管理应用 Kindle Mate 只支持 Windows,不支持 Mac.标注只是解析我的剪贴文本文件,配合 FileReader ...

  10. Ubuntu安装32位程序兼容包

    有的交叉编译工具链是32位的,经常会遇到安装完成之后提示好不到,这时候需要安装32位兼容程序,使用以下命令安装: sudo apt-get update sudo apt install gcc-mu ...