题目来源:洛谷

题目描述

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

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

输入输出格式

输入格式:

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

输出格式:

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

输入输出样例

输入样例#1:

4
4 5 9 4
输出样例#1:

43
54

解析:

这是一道相当经典的四边形不等式和环形dp,以至于一道NOI级别的题目被研究成了绿题。。。


首先题目意思很清楚,很容易看出来这就是个首尾相接的环。

 
 
我们先想一下如果不是环,而是一条链怎么做。
 
dp的思路很容易想出来,就是区间dp的思想,我们将将要合并的两堆石子的距离当做阶段,合并哪两堆石子作为状态,决策就是选择哪两堆石子合并。
 
我们定义dp[l][r]表示合并l~r之间的石子所需耗费的最小/大费用,其中l表示左端点的石子,r表示右端点的石子。
 
我们可以把每个l~r的状态看做两个子问题,假设k为l~r之间的任意一堆石子,我们就要使得l~k的答案和k+1~r的答案最优。因此我们枚举l~r之间的每个位置k,并寻找使得结果最优的一个k。
对应到动态规划中,就意味着两个较小的区间向一个更长的区间转移,k点不仅是两个小区间的划分点,也是转移的决策。
 
比方说如果我们求最小值,得出这样的状态转移方程:

dp[l][r]=min(1<=l<=n-len+1,l<r<=n,l<=k<r){dp[l][k]+dp[k+1][r]+w(l,r)};

其中w(l,r)函数表示合并l~r之间的石子的花费,len表示区间长度

这个东西叫二维四边形不等式,常用于优化区间dp,详细情况我这里就不讲了(我懒),如果有需要可以百度或者戳这里看dalao博客。

PS:关于初始化,我们知道每个石子堆移到它自己上面花费是0,所以。。。

思考一下为什么每次决策增加的是区间和:移动石子堆的花费是可叠加的,而且费用是会重叠的,如果我们要合并l~r之间的所有石子,那么显然无论它之前是由哪两堆合并而来的,花费都是sum[r]-sum[l-1](sum为前缀和数组)。

关于w(l,r)函数,我们可以用前缀和优化搞定,于是我们需要一个三重循环来解决此问题,复杂度也就差不多达到了O(n^3),这是无法承受大规模数据的。

动态规划有一种基本的优化思路,就是如果一个状态的某个维度可以由其他维度推出,那么我们直接在每循环的时候把它算出来就得了。

在这里,如果我们知道了左端点l和区间长度len的话,那么r就是可求得的,即r=l+len-1;

至此,链式dp的思路就讲完了。


你如果拿这个思路做这道题,会发现过不了(WA4个点),这是因为题目说了是环。

那环跟链有啥区别呢?

很容易发现,如果是环形的话,某两个点之间的距离可以有两种可能。

举个例子,我们假设石子总堆数为n,对于第一堆石子到最后一堆石子之间的距离len,如果是链式的话,只有n-1这种可能;而如果是环形的话,我们就有n-1和1两种可能。也就是说,每个石子堆之间都有两个距离(当然可以相等),那我们怎么设计dp算法呢?

有一种普遍的解法,就是把环拆开成相连的两条相同的链,然后在1~2n上跑dp。具体来说其实就是把上面讲到的两个距离拆出来分开处理,某个点l到第一条链有一个最优解,到第二条链又有一个最优解,我们再取这两个最优解的最优解。

所以之前的步骤我们可以参考链式dp写出来,而寻找两个最优解中的最优解,我们需要再分析一下。

链式的解法最优解就在dp[1][n],即把1~n堆石子全部合并。注意,这种情况下有且仅有1~n之间的距离为n-1

然而如果这是一个环,就有n个n-1的距离,也就是有n个局部最优解,我们还要在这些最优解里面找全局最优解,这也是本题的又一个关键点。

所以实际上我们扫一遍dp[i][i+n-1](1<=i<=n),找到最优解就行了。

参考代码:

 #include<cstdio>
#include<iostream>
#include<cstring>
#define N 1010
#define INF 0x3f3f3f3f
using namespace std;
int a[N<<],dp1[N][N],dp2[N][N],s[N];
int main()
{
int n;
cin>>n;
for(int i=;i<=n;i++) scanf("%d",&a[i]),a[i+n]=a[i];
memset(dp1,0x3f,sizeof(dp1));
memset(dp2,0xff,sizeof(dp2));
for(int i=;i<=n+n;i++){
dp1[i][i]=;dp2[i][i]=;
s[i]=s[i-]+a[i];
}
for(int len=;len<=n*;len++)
for(int l=;l<=n*-len+;l++)
{
int r=l+len-;
for(int k=l;k<r;k++)
{
dp1[l][r]=min(dp1[l][r],dp1[l][k]+dp1[k+][r]+(s[r]-s[l-]));
dp2[l][r]=max(dp2[l][r],dp2[l][k]+dp2[k+][r]+(s[r]-s[l-]));
}
}
int maxx=-INF,minn=INF;
for(int i=;i<=n;i++){
maxx=max(maxx,dp2[i][i+n-]);
minn=min(minn,dp1[i][i+n-]);
}
cout<<minn<<endl;
cout<<maxx<<endl;
return ;
}

P1880 [NOI1995]石子合并[环形DP]的更多相关文章

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

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

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

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

  3. Luogu【P1880】石子合并(环形DP)

    先放上luogu的石子合并题目链接 这是一道环形DP题,思想和能量项链很像,在预处理过程中的手法跟乘积最大相像. 用一个m[][]数组来存储石子数量,m[i][j]表示从第 i 堆石子到第 j 堆石子 ...

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

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

  5. HDU4632 Poj2955 括号匹配 整数划分 P1880 [NOI1995]石子合并 区间DP总结

    题意:给定一个字符串 输出回文子序列的个数    一个字符也算一个回文 很明显的区间dp  就是要往区间小的压缩! #include<bits/stdc++.h> using namesp ...

  6. P1880 [NOI1995]石子合并 区间dp+拆环成链

    思路 :一道经典的区间dp  唯一不同的时候 终点和起点相连  所以要拆环成链  只需要把1-n的数组在n+1-2*n复制一遍就行了 #include<bits/stdc++.h> usi ...

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

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

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

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

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

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

随机推荐

  1. 开启Hadoop和Spark的学习之路

    Hadoop Hadoop是一个由Apache基金会所开发的分布式系统基础架构. 用户可以在不了解分布式底层细节的情况下,开发分布式程序.充分利用集群的威力进行高速运算和存储. Hadoop实现了一个 ...

  2. Echart timeline 高级用法!!!!

    一.前言 在使用 echart timeline 来着图形可视化时,我使用的和官网也不一样,因为我有使用映射关系.比如我将 no 映射到X轴,将 d4 映射到Y轴. 二.参考 echart官网:htt ...

  3. ztree节点名称排序

    // result 为后台返回的集合,在渲染tree前的数据 result = result.sort(function (a, b) { // 判断前面一个是字母,后面一个不是字母,那么不换位置,返 ...

  4. js控制数量包含截取

    <div class="usermes_index_line"> 进行中的单 <div id="usermes_index_line_i2"& ...

  5. php密码正则匹配

    <?php /** * @param $pwd * @return string */ function isPwd($pwd){ $pattern= '/^[0-9a-z_$]{6,16}$/ ...

  6. LeetCode 108. 将有序数组转换为二叉搜索树(Convert Sorted Array to Binary Search Tree) 14

    108. 将有序数组转换为二叉搜索树 108. Convert Sorted Array to Binary Search Tree 题目描述 将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索 ...

  7. python安装OpenCV – 4.1.0

    (python3) [jiangshan@localhost ~]$ pip install opencv_python==4.1.0Collecting opencv_python==4.1.0 E ...

  8. 034 Android NavigationView和DrawerLayout实现抽屉式导航设计(侧边栏效果)

    1.创建带侧滑效果的activity 右击,new---->activity---->选择NavgationDrawer Activity 2.xml文件布局 (1)activity_ma ...

  9. Python--递归函数实现:多维嵌套字典数据无限遍历

    原创:多层嵌套字典无限遍历,实现当value值以特殊字符$开头,并且等于某项值时,用随机函数替换该参数 """处理前的字典{'patient': {'avatarPic' ...

  10. Python中的动态属性与描述符

    动态属性与属性描述符 属性描述符是什么?   在解释属性查找顺序之前我们需要了解Python中的属性描述符,属性描述符作为其他类对象的属性而存在,实现了特殊方法中的get.set.delete中的一种 ...