沙子合并加强

沙子合并问题

问题描述:设有N堆沙子排成一排,其编号为1,2,3,…,N(N<=2000)。每堆沙子有一定的数量,可以用一个整数来描述,现在要将这N堆沙子合并成为一堆,每次只能合并相邻的两堆,合并的代价为这两堆沙子的数量之和,合并后与这两堆沙子相邻的沙子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同,如有4堆沙子分别为 1  3  5  2 我们可以先合并1、2堆,代价为4,得到4 5 2 又合并 1,2堆,代价为9,得到9 2 ,再合并得到11,总代价为4+9+11=24,如果第二步是先合并2,3堆,则代价为7,得到4 7,最后一次合并代价为11,总代价为4+7+11=22;问题是:找出一种合理的方法,使总的代价最小。输出最小代价。

输入:

第一行一个数N表示沙子的堆数N。

第二行N个数,表示每堆沙子的质量。

输出:

合并的最小代价以及每一步合并的方法(输出每次合并后的沙子的最小编号和最大编号)

样例:

输入:

4

13 7 6 5

输出:

60

3 4

2 4

1 4

来,先看看,找不同

总结一下第一题数据范围300,第二题范围不变,但是变成了一个环,第三题数据范围变成了2000

朴素的沙子合并算法

dp(i,j)表示把i到j这一段沙子合并成为一堆沙子,所需要的最小代价

那么一定是由某两堆合并而来的

所以dp(i,j)=min{dp(i,k)+dp(k+1,j)}+sum[j]-sum[i-1]

这样的算法O(n^3)只能过第一个吧

没事,老大被解决了

第二题是环形的

如果每个起始位置都被枚举一遍的话

就是O(n^4),过不了,所以,我们力求一个更加高效的算法。

解决问题题的方法有两种,一种是继承,这里不讨论,还有一种是展环为链(一种解决环形dp的最佳方法)

我们复制序列一遍,将它粘贴在第一个序列的末尾,构成2n-1的序列,然后对这个序列做区间dp

所以最佳解一定是一个子问题,这下子复杂度最高(2n-1)^2,可以勉勉强强的过吧

然后第三题就麻烦了n<=2000

n^3绝对超时,肿么办呢。

这就是一套全新的理论,四边形优化

理论如下

DP的四边形优化

一、进行四边形优化需要满足的条件

1、状态转移方程如下:

m(i,j)表示对应i,j情况下的最优值。

w(i,j)表示从i到j的代价。

例如在合并石子中:

m(i,j)表示从第i堆石子合并到j堆石子合并成一堆的最小代价。

w(i,j)表示从第i堆石子到第j堆石子的重量和。

2、函数w满足区间包含的单调性和四边形不等式

二、满足上述条件之后的两条定理

1、假如函数w满足上述条件,那么函数m也满足四边形不等式,即

例如:

假如有:w(1, 3) + w(2, 4) £ w(2, 3) + w(1, 4),

m(1, 3) + m(2, 4) £ m(2, 3) + m(1, 4),

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

m(i,j)=min{m(i,k-1),m(k,j)}+w(i,j)(i≤k≤j)(min也可以改为max)

上述的m(i,j)表示区间[i,j]上的某个最优值。w(i,j)表示在转移时需要额外付出的代价。该方程的时间复杂度为O(N3)

下面我们通过四边形不等式来优化上述方程,首先介绍什么是“区间包含的单调性”和“四边形不等式”

1、区间包含的单调性:如果对于 i≤i'

2、四边形不等式:如果对于 i≤i'

下面给出两个定理:

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)。

好了,有了上述的两个定理后,我们发现如果w函数满足区间包含单调性和四边形不等式性质,那么有 s(i,j-1)≤s(i,j)≤s(i+1,j) 。

即原来的状态转移方程可以改写为下式:

m(i,j)=min{m(i,k-1),m(k,j)}+w(i,j)(s(i,j-1)≤k≤s(i+1,j))(min也可以改为max)

由于这个状态转移方程枚举的是区间长度 L=j-i,而 s(i,j-1) 和 s(i+1,j) 的长度为 L-1,是之前已经计算过的,可以直接调用。

不仅如此,区间的长度最多有n个,对于固定的长度 L,不同的状态也有 n 个,故时间复杂度为 O(N^2),而原来的时间复杂度为 O(N^3),实现了优化!

今后只需要根据方程的形式以及 w 函数是否满足两条性质即可考虑使用四边形不等式来优化了。

以上描述状态用 m(i,j),后文用的 dp[i][j],所代表含意是相同的,特此说明。

以石子合并问题为例。

例如有6堆石子,每堆石子数依次为3 4 6 5 4 2

因为是相邻石子合并,所以不能用贪心(每次取最小的两堆合并),只能用动归。(注意:环形石子的话,必须要考虑最后一堆和第一堆的合并。)

例如:一个合并石子的方案:

第一次合并 3 4 6 5 4 2 ->7

第二次合并 7 6 5 4 2 ->13

第三次合并 13 5 4 2 ->6

第四次合并 13 5 6 ->11

第五次合并 13 11 ->24

总得分=7+6+11+13+24=61 显然,比贪心法得出的合并方案(得分:62)更优。

动归分析类似矩阵连乘等问题,得出递推方程:

设 dp[i][j] 表示第 i 到第 j 堆石子合并的最优值,sum[i][j] 表示第 i 到第 j 堆石子的总数量。

(可以在计算开始先做一遍求所有的 sum[i],表示求出所有第1堆到第i堆的总数量。则 sum[i][j]=sum[j]-sum[i]。这样计算比较快。)

那么就有状态转移公式:

这里 i<=k

普通解法需要 O(n^3)。下面使用四边形不等式进行优化。

首先判断是否符合区间单调性和四边形不等式。

i  i'    j    j'

3 4 6 5 4 2

单调性:

w[i',j] = 4+6+5=15 w[i,j'] =3+4+6+5+4+2=24

故w[i',j] <= w[i,j'] 满足单调性

四边形不等式:

w[i,j] + w[i',j'] = (3+4+6+5) + (4+6+5+4+2) = 18+21 = 39

w[i',j] + w[i,j'] = (4+6+5) + (3+4+6+5+4+2) = 15 + 24 = 39

故 w[i,j] + w[i',j'] <= w[i',j] + w[i,j']

故石子合并可利用四边形不等式进行优化。

利用四边形不等式,将原递推方程的状态转移数量进行压缩(即缩小了k的取值范围)。

令 s[i][j]=min{k | dp[i][j] = dp[i][k-1] + dp[k][j] + w[i][j]},即计算出 dp[i][j] 时的最优的 k 值(本例中寻优为取最小)

也可以称为最优决策时的 k 值。由于决策 s 具有单调性,因此状态转移方程中的 k 的取值范围可修改为 :

s[i,j-1] <= s[i,j] <= s[i+1,j]

边界:s[i,i] = i

因为 s[i,j] 的值在 m[i,j] 取得最优值时,保存和更新,因此 s[i,j-1] 和 s[i+1,j] 都在计算 dp[i][j-1] 以及 dp[i+1][j] 的时候已经计算出来了。

因此,s[i][j] 即 k 的取值范围很容易确定。

根据改进后的状态方程,以及 s[i][j] 的定义方程,可以很快的计算出所有状态的值。计算过程可以如下表所示(类似于矩阵连乘的打表)。

状态表(如果是环形石子合并,需要打2n*2n的表)

3 4 6 5 4 2

例如:

计算dp[1][3],由于s[1][2]=1,s[2][3]=2,则k值的取值范围是1<=k<=2

则,dp[1][3]=min{dp(1,1)+dp(2,3)+13, dp(1,2)+dp(3,3)+13}=min{10+13, 7+13}=20,将其填到状态表。同时,由于取最优值的k等于2,则将其填到s表。

同理,可以计算其他状态表和s表中的值。

dp[2][4]=min{dp(2,2)+dp(3,4)+15, dp(2,3)+dp(4,4)+15}=min{11+15, 10+15}=25

k=3

从表中可以看出,当计算dp[2][5]的时候,由于s[ i,j-1]=s[ 2,4]=3,s[ i+1,j]=s[3,5]=3,此时k的取值范围已经限定为只有一个,大幅缩短了寻找最优解的时间。

于是乎,复杂度降到了O(n^2)

厉害吧O(∩_∩)O哈哈~

附上代码

#include#include#include#include#include#include#include#include#include#include#include

#define mod 998244353

#define N 100005

#define pi acos(-1)

#define inf 0x7fffffff

#define ll long long

using namespacestd;

ll read()

{

ll x=0,f=1;char ch=getchar();while(ch'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;

}intn;int a[305],sum[305];int f[305][305];int dp(int l,intr)

{if(f[l][r]!=-1)returnf[l][r];if(l==r)return 0;int ans=inf;for(int i=l;i

ans=min(ans,dp(l,i)+dp(i+1,r));return f[l][r]=(ans+sum[r]-sum[l-1]);

}intmain()

{

memset(f,-1,sizeof(f));

n=read();for(int i=1;i<=n;i++)a[i]=read();for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i];

printf("%d\n",dp(1,n));return 0;

}

#include#include

using namespacestd;int v[1001],sum[1001],f[1001][1001];intmain()

{int n,mi=9999999;

scanf("%d",&n);for(int i=1;i<=2*n-1;i++)for(int j=i+1;j<=2*n-1;j++)

f[i][j]=999999;int p=0;for(int i=1;i<=n;i++)

{

scanf("%d",&v[i]);

sum[i]=sum[i-1]+v[i];

}for(int i=1;i

{

v[++p]=v[i];

sum[p+n]=sum[p+n-1]+v[i];

}for(int i=2*n-1;i>=1;i--)for(int j=i+1;j<=i+n-1;j++)for(int k=i;k

f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1]);for(int i=1;i<=n;i++)

mi=min(mi,f[i][i+n-1]);

printf("%d",mi);return 0;

}

#include#include

#define N 2000+1

#define INF 0x3fffffff

using namespacestd;intv[N],sum[N];structdata{intval,des;

};

data f[N][N];void output(int k,int i,intj);intmain()

{intn;

scanf("%d",&n);for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)

f[i][j].val=INF;for(int i=1;i<=n;i++)

{

scanf("%d",&v[i]);

sum[i]=sum[i-1]+v[i];

f[i][i].des=i;

}for(int i=n;i>=1;i--)for(int j=i+1;j<=n;j++)

{int begin=f[i][j-1].des;int end=min(j-1,f[i+1][j].des);for(int k=begin;k<=end;k++)

{if(f[i][j].val>f[i][k].val+f[k+1][j].val+sum[j]-sum[i-1])

{f[i][j].des=k;f[i][j].val=f[i][k].val+f[k+1][j].val+sum[j]-sum[i-1];}

}

}

printf("%d\n",f[1][n].val);

output(f[1][n].des,1,n);//printf("%d",f[1][n].des);

return 0;

}void output(int k,int i,intj)

{if(i==j)return;

output(f[i][k].des,i,k),output(f[k+1][j].des,k+1,j);

printf("%d %d\n",i,j);

}

java怎么做沙子合并_dp之沙子合并 环形沙子合并 沙子合并加强 沙子三兄弟的故事...相关推荐

  1. 学完Java可以做什么兼职?去哪里找兼职?

    最近有些同学表示我感觉自己Java可学的差不多了,想要找一些兼职一方面锻炼自己,一方面还能赚点外快,那么学Java的到底应该怎么做兼职呢?本篇来解答一下这个问题. 学完Java可以做什么兼职? 能做的 ...

  2. 零基础学Java需要做哪些准备

    想要成为一名合格的java工程师,那么好好学习java技术是非常重要的,对于零基础同学们来说,大家比较关注的就是"零基础学Java需要做哪些准备"这个问题,下面小编就来为大家做下详 ...

  3. 小猿圈讲解Java可以做什么?

    前几天有个小伙伴问我学完Java可以做什么?对于大部分人只知道Java是编程语言的一种,但是具体的能做什么确不是很了解,针对这样的现象,小猿圈给大家讲解一下Java可以做什么? 1. 大数据 大数据领 ...

  4. Java能做什么?学完Java可以从事什么工作呢?

    如果你是一个Java初学者,你可能对Java应用在什么地方感到困惑.除了"马里奥""贪吃蛇"等经典游戏,其他领域好像也找不到Java的踪迹,那么Java究竟能做 ...

  5. java多线程做一件事_关于Java的十件事

    java多线程做一件事 那么,您从一开始就一直在使用Java? 还记得那些被称为" Oak"的日子,OO仍然是热门话题,C ++人士认为Java没有机会,Applet还是一件事吗? ...

  6. java pdf无法加载_java - 试图使用iText7合并来合并pdf,但是当我打开最终的合并pdf时,它说无法加载pdf文档 - SO中文参考 - www.soinside.com...

    试图使用iText7合并来合并pdf,但是当我打开最终的合并pdf时,它说无法加载pdf文档 问题描述 投票:0回答:1 我正在使用itext7 PdfWriter创建两个ByteArrayOutpu ...

  7. Java 接口做参数,接口回调

    JAVA接口做参数,接口回调 接口做参数:将实现某接口的类的对象的引用用作参数传递给该接口参数.该接口通过回调来实现该接口方法. 接口回调:实现某接口的类的对象的引用,赋值给该接口声明的接口变量. 难 ...

  8. Java好学吗?Java能做什么?如何快速入门Java?

    Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承.指针等概念,Java具有简单性.面向对象.分布式.健壮性.安全性.平台独立与可移植性.多线程.动态性等 ...

  9. 学java怎么做兼职?学Java什么程度才能找兼职?

    生活中很多的程序都是用Java编写而成的,正因如此才有这么多学习Java的同学,有些同学表示我感觉自己学的差不多了,想要找一些兼职一方面锻炼自己,一方面还能赚点外快,那么学Java的到底应该怎么做兼职 ...

最新文章

  1. 单纯形法(二)(线性规划的基本定理)
  2. 计算机怎么设置本地硬盘启动不了,电脑bios怎么设置硬盘启动
  3. c#_序列化与反序列化的应用
  4. luogu P5324 [BJOI2019]删数
  5. fprintf/fscanf函数分析
  6. C#内存流示例-----gt;用内存流来读取图片
  7. 关于ElasticSearch整合SpringBoot
  8. Looking Back 2018
  9. 麦克纳姆轮全向移动机器人横向直线运动分析
  10. POJ3991 HDU3351 UVALive4733 Seinfeld【水题】
  11. php与mysql对接_PHP与MySql建立连接
  12. 泽林主办前沿IT技术分享峰会隆重召开,深度探讨人工智能、大数据与物联网 的未来发展趋势
  13. 【搭建OpenCV+Tesseract】
  14. PHP中的定界符 echo
  15. python列表转集合_Pandas把dataframe或series转换成list的方法
  16. python的胶水特性是怎样出来的_为什么说python是胶水语言
  17. 【Todo】【转载】深度学习神经网络 科普及八卦 学习笔记 GPU SIMD
  18. 1032 挖掘机技术哪家强 (20 分)(测试点分析)
  19. 从稻农成长为技术领导者
  20. c# 导入Excel 存到DataTable并进行行转列操作及合并DataTable相同行的值

热门文章

  1. 学生信息的添加与查询_JAVA
  2. 6.分布式数据库HBase第2部分
  3. 【Linux】5.linux下的export命令和环境变量
  4. Precision和Recall
  5. Java数据结构和算法(六)——前缀、中缀、后缀表达式
  6. Java Class 文件格式及其简单 Hack
  7. LaTeX集合运算相关命令
  8. 日文 LaTeX 系统介绍 - 最简示例
  9. OpenCV之feature2d 模块. 2D特征框架(1)Harris 角点检测子 Shi-Tomasi角点检测子 定制化创建角点检测子 亚像素级的角点检测 特征点检测
  10. 简明python教程 --C++程序员的视角(五):面向对象的编程