我们来看这样一道题目

有n堆石子排成一列,每堆石子有一个重量w[i],
每次合并可以合并相邻的两堆石子,一次合并的代价为两堆石子的重量和w[i]+w[i+1]。问安排怎样的合并顺序,能够使得总合并代价达到最小。
n<=100,w<=100

显然这是一道普及水平的区间DP
fi..jf_{i..j}表示将[i,j]合并成一堆所需要的最小代价
ai表示每堆石子的数量a_i表示每堆石子的数量
sumi=∑ij=1aisum_i=\sum ^{i}_{j=1}a_i
fi..j=fi..k+fk+1..j+sumj−sumi−1f_{i..j}=f_{i..k}+f_{k+1..j}+sum_j-sum_{i-1}
正确做法是O(n3)O(n^3)

#include<cstdio>
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int w[101],n,f[102][102];
main()
{int x;scanf("%d",&n); for (int i=1;i<=n;i++)scanf("%d",w+i),w[i]+=w[i-1];memset(f,63,sizeof(f));for (int i=1;i<=n;i++) f[i][i]=0;for (int i=n-1;i>=1;i--)for (int j=i+1;j<=n;j++)for (int k=i;k<=j-1;k++)f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+w[j]-w[i-1]);printf("%d",f[1][n]);
}

我们注意到,这是石子堆排列成一条线的
那如果这些石子堆是排成一个圆呢?
我们可以这么想
每两个相邻的石子堆之间连一条线段,那样每个石子堆都往外发出两条线段,每次合并(i,j)实际上是砍掉i
到j的线段,原先i到i的前一个石子堆的线段变成了j到i的前一个石子堆的线段
最初是n条线段,合并n-1次,最后肯定有一个线段用不到
直线排列时这条用不到的线段是已知的
但圆排列时是未知的
那我们就尝试枚举每一条所谓的线段
把这些石子堆当成直线排列,然后后面再加上一条排列相同的石子堆!
最终答案就是minfi..i+n−1minf_{i..i+n-1} i=1..ni=1..n
复杂度仍是O(n3)O(n^3)

#include<cstdio>
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int w[201],n,f[202][202];
main()
{int x;scanf("%d",&n); for (int i=1;i<=n;i++)scanf("%d",w+i),w[i+n]=w[i];for (int i=1;i<=n<<1;++i) w[i]+=w[i-1];memset(f,63,sizeof(f));for (int i=1;i<=n<<1;i++) f[i][i]=0;for (int i=n<<1;i>=1;i--)for (int j=i+1;j<=n<<1;j++)for (int k=i;k<=j-1;k++)f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+w[j]-w[i-1]);f[0][0]=1<<30;for(int i=1;i<=n;++i)f[0][0]=min(f[0][0],f[i][i+n-1]);printf("%d\n",f[0][0]);
}

求最大值类似,不再赘述
那我们变一下n的范围
n<=3000
那我们需要O(n2)O(n^2)做法
怎么办?
考虑四边形不等式优化

大神比我讲的更清楚

    1、区间包含的单调性:如果对于 i≤i’ <j≤j’,有 w(i’,j)≤w(i,j’),那么说明w具有区间包含的单调性。(可以形象理解为如果小区间包含于大区间中,那么小区间的w值不超过大区间的w值)

    2、四边形不等式:如果对于 i≤i’<j≤j’,有 w(i,j)+w(i’,j’)≤w(i’,j)+w(i,j’),我们称函数w满足四边形不等式。(可以形象理解为两个交错区间的w的和不超过小区间与大区间的w的和)

  下面给出两个定理:

    1、如果上述的 w 函数同时满足区间包含单调性和四边形不等式性质,那么函数 m 也满足四边形不等式性质

      我们再定义 s(i,j) 表示 m(i,j) 取得最优值时对应的下标(即 i≤k≤j 时,k 处的 w 值最大,则 s(i,j)=k)。此时有如下定理

    2、假如 m(i,j) 满足四边形不等式,那么 s(i,j) 单调,即 s(i,j)≤s(i,j+1)≤s(i+1,j+1)。

总之我们就可以优化成O(n2)O(n^2)的了

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n,w[3005];
int f[3005][3005],s[3005][3005];
main()
{scanf("%d",&n);memset(f,63,sizeof(f));for (int i=1;i<=n;++i)scanf("%d",w+i),f[i][i]=0,s[i][i]=i,w[i]+=w[i-1];for (int i=n;i>=1;--i)for (int j=i+1;j<=n;++j)for (int k=s[i][j-1];k<=s[i+1][j];++k)if (f[i][j]>f[i][k]+f[k+1][j]+w[j]-w[i-1])f[i][j]=f[i][k]+f[k+1][j]+w[j]-w[i-1],s[i][j]=k;printf("%d\n",f[1][n]);
}

环形类似
那求最大值呢?
这好像不太符合四边形不等式的优化
但仍然可以O(n2)O(n^2)
以cogs上石子归并加强版的环形为例

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n,w[4005];
int f[4005][4005],s[4005][4005];
main()
{freopen("stone3.in","r",stdin);freopen("stone3.out","w",stdout);scanf("%d",&n);for (int i=1;i<=n;++i)scanf("%d",w+i),s[i+n][i+n]=i+n,s[i][i]=i,w[n+i]=w[i];for (int i=1;i<=n*2;++i) w[i]+=w[i-1];for (int i=n*2-1;i>=1;--i)for (int j=i+1;j<=min(n*2-1,i+n-1);++j)f[i][j]=max(f[i][i]+f[i+1][j],f[j][j]+f[i][j-1])+w[j]-w[i-1];for(int i=1;i<=n;++i) f[0][0]=max(f[0][0],f[i][i+n-1]);printf("%d\n",f[0][0]);
}

注意这是严格O(n2)O(n^2)
那我们来个终极版
n<=50000,求最小值
怎么办?
考虑GarsiaWachs算法
ACDreamer

#include<cstdio>
#define M 50001
using namespace std;
int n,a[M],tot,ans;
int in()
{int t=0;char ch=getchar();while (ch>'9'||ch<'0') ch=getchar();while (ch>='0'&&ch<='9') t=(t<<1)+(t<<3)+ch-48,ch=getchar();return t;
}
void unions(int x)
{int tmp=a[x]+a[x-1];ans+=tmp;for (int i=x;i<tot;++i) a[i]=a[i+1];int j;for (j=x-1;a[j-1]<tmp&&j>1;--j) a[j]=a[j-1];a[j]=tmp;--tot;for (int d=tot-j;j>2&&a[j-2]<=a[j];d=tot-j)unions(j-1),j=tot-d;
}
main()
{n=in();for (int i=1;i<=n;++i) a[i]=in();for (int i=1;i<=n;++i){a[++tot]=a[i];while (tot>2&&a[tot-2]<=a[tot]) unions(tot-1);}for (;tot>1;) unions(tot);printf("%d\n",ans);
}

据说这个是O(n2)O(n^2)的,但是在一般数据下它可以到达O(nlogn)O(nlogn)
这本来是应该平衡树做的
写得很匆忙而且水平很渣
找个时间再来仔细研究……

石子合并问题(内含BZOJ3229 AC代码)相关推荐

  1. CSP认证201612-4 压缩编码[C++题解]:区间dp、huffman树、石子合并

    题目分析 来源:acwing 分析: 本题难在想到是区间dp.想到区间dp之后,这就是石子合并的代码直接默写. 那么是如何建模的呢?我们把huffman编码(这里要求按照字典序大小编码,和huffma ...

  2. 單子の题解:直线石子合并

    單子の题解:直线石子合并 题目描述 题目核心 代码实现 题目描述 古娜拉黑暗之神. 题目核心 状态表示:dp[i][j]表示从i到j的合并最小代价 列举每个小区间i,j的代价 左右两堆合成的最小代价= ...

  3. 算法基础课-动态规划-区间dp-AcWing 282. 石子合并:区间dp

    文章目录 题目分析 题目链接 题目分析 只能合并相邻两堆.求体力最小值 数据比较弱,最多300堆,每堆重量不超过1000. 状态表示 f[i][j]表示合并区间[i,j]需要的最小体力 状态转移 把区 ...

  4. 【算法设计与分析】经典常考三十三道例题AC代码

    ❥小虾目前大三,我校在大一下开设<数据结构>这门课,大二上开了<算法设计与分析>这门课,很庆幸这两门课的上机考试总成绩一门100,一门99,最后总分也都90+.下文会给出机试的 ...

  5. 石子合并的GarsiaWachs算法

    石子合并的GarsiaWachs算法 2010-07-28 18:55:51|  分类: 程序 |字号 订阅 石子合并(每次合并相邻的两堆石子,代价为这两堆石子的重量和,把一排石子合并为一堆,求最小代 ...

  6. P1880 [NOI1995]石子合并

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

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

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

  8. 7-10 石子合并 (10 分)

    一:前言: 关于石子合并,这个问题分为链型和环形两种,本题当中用的是环形,但我们在学习动态规划,所以多练肯定是有好处的,况且将链型的问题解决后,更容易理解环形的解决方法,所以本次题解分为两部分链型和环 ...

  9. AcWing 1068. 环形石子合并

    AcWing 1068. 环形石子合并 题意: n堆石头围成一个圈,然后将相邻两堆合并成新的一堆,得分为新的一堆的石头数 问最高得分合最低得分 题解: 很简单,区间dp的模板题 和这个题一样 状态转移 ...

最新文章

  1. NotePad++列编辑
  2. python opencv 批量将视频转化为图片
  3. 全球及中国素食宠物食品行业产销需求与销售渠道咨询报告2022版
  4. 微信小程序小结(3) -- 使用wxParse解析html及多数据循环
  5. HLSL Tips 1:如何把输入映射到输出像素
  6. jQuery -- 光阴似箭(三):jQuery 操作 HTML 元素和属性
  7. 空间相关分析与SDM
  8. Excel - 透视表 - 组合 分组
  9. 苹果手机从企业微信进入小程序下载不了图片
  10. laravel框架基础知识
  11. oracle mod函数
  12. HOJ 1276 士兵队列训练问题(stl, 水题)
  13. 关于硬盘分区(主分区、扩展分区和逻辑分区)
  14. title显示不全的处理方法
  15. iOS开发笔记--Facebook POP 进阶指南
  16. csapp-bomblab
  17. 白话Elasticsearch11-深度探秘搜索技术之基于tie_breaker参数优化dis_max搜索效果
  18. 湖南芒果代理php,芒果商城系统GSHOP php网店系统 UTF8
  19. 京东一键复制宝贝,如何快速上货?
  20. P(NIPAM-co-AdMA)/PtBMA-b-PNIPAM聚(甲基)丙烯酸叔丁酯-b-聚(N-异丙基丙烯酰胺)

热门文章

  1. 1个月时间整理了2019年上千道Java面试题,近500页文档!
  2. Linux单用户下提示权限不够,Linux_/dev/null 权限不足,/dev/null文件设备的权限不对, - phpStudy...
  3. mysql dba命令_mysql DBA:mysqladmin常用命令总结
  4. 最小生成树 Kruskal算法 Prim算法
  5. bitcoin jsonrpc java_BitcoinCore JSONRPC Java使用创建账号,获取余额,转账等等...
  6. 彻底理解MapReduce shuffle过程原理
  7. 如何使用IntelliJ IDEA搭建spark开发环境(上)
  8. linux uart中断程序,linux 串口中断实现
  9. c# mysql 链接池溢出_C#MySQL连接池限制,并清理连接
  10. 虚拟服务器怎么进去,怎么进入虚拟主机