石子合并-直线版

(点击此处查看题目)

朴素写法

最简单常见的写法就是通过枚举分割点,求出每个区间合并的最小花费,从而得到整个区间的最小花费,时间复杂度为O(n^3),核心代码如下:

        for (int i = ; i < n; i++)
{
for (int j = ; j + i <= n; j++)
{
int e = j + i;
dp[j][e] = inf;
for (int k = j; k + <= e; k++)
{
dp[j][e] = min(dp[j][e], dp[j][k] + dp[k + ][e] + sum[e] - sum[j - ]);
}
}
}

四边形优化

对于函数f[][](在这里可以看作数组),如果满足 f[a][c] + f[b][d] <= f[b][c] + f[a][d],则f满足四边形不等式,即 f[i][j-1] <= f[i][j] <= f[i+1][j]

记s[i][j]表示取得dp[i][j]的最优解的分割点,稍加证明即可发现其满足四边形不等式

注意到,在朴素写法中,我们需要枚举分割点来求解:dp[i][j] = max(dp[i][j],dp[i][k]+dp[k+1][j] + sum[j] - sum[i-1]) {i <= k <= j ) ,而因为 s[i][j-1] <= s[i][j] <= s[i+1][j],所以我们枚举的最优分割点s[i][j]的范围即[ s[i][j-1],s[i+1][j] ] ,所以我们枚举分割点的时候,就直接枚举区间[ s[i][j-1],s[i+1][j] ] 而不用枚举[i,j],这样一来,时间复杂度就是O(n^2),核心代码如下:

    for (int i = ; i <= n; i++)                //枚举右端点-左端点的值
{
for (int j = ; j + i <= n; j++) //枚举区间左端点
{
int e = j + i; //区间右端点
dp[j][e] = inf;
for (int k = s[j][e - ]; k <= s[j + ][e]; k++)
{
if (dp[j][e] > dp[j][k] + dp[k + ][e] + sum[e] - sum[j - ])
{
dp[j][e] = dp[j][k] + dp[k + ][e] + sum[e] - sum[j - ];
s[j][e] = k;
}
}
}
}

GarsiaWachs算法

这个方法是我觉得最优的解法了,时间复杂度为O(nlogn),比四边形优化更快,具体原理理解不足,就先贴上大佬的解析吧

解决这类问题的大概步骤是:

0.证明w满足四边形不等式,这里w是m的附属量,形如m[i,j]=opt{m[i,k]+m[k,j]+w[i,j]},此时大多要先证明w满足条件才能进一步证明m满足条件

1.证明m满足四边形不等式

2.证明s[i,j-1]≤s[i,j]≤s[i+1,j]

设序列是stone[],从左往右,找一个满足stone[k-1] <= stone[k+1]的k,找到后合并stone[k]和stone[k-1],再从当前位置开始向左找最大的j,使其满足stone[j] > stone[k]+stone[k-1],插到j的后面就行。一直重复,直到只剩下一堆石子就可以了。在这个过程中,可以假设stone[-1]和stone[n]是正无穷的。

举个例子:186 64 35 32 103

因为35<103,所以最小的k是3,我们先把35和32删除,得到他们的和67,并向前寻找一个第一个超过67的数,把67插入到他后面,得到:186 67 64 103,现在由5个数变为4个数了,继续:186 131 103,现在k=2(别忘了,设A[-1]和A[n]等于正无穷大)234 186,最后得到420。最后的答案呢?就是各次合并的重量之和,即420+234+131+67=852。

基本思想是通过树的最优性得到一个节点间深度的约束,之后证明操作一次之后的解可以和原来的解一一对应,并保证节点移动之后他所在的深度不会改变。具体实现这个算法需要一点技巧,精髓在于不停快速寻找最小的k,即维护一个“2-递减序列”朴素的实现的时间复杂度是O(n*n),但可以用一个平衡树来优化,使得最终复杂度为O(nlogn)。
(解析出自:https://blog.csdn.net/bao___zi/article/details/81913096)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#include<stack>
#include <map>
#include <iomanip> #define bug cout << "**********" << endl
#define show(x, y) cout<<"["<<x<<","<<y<<"] "
#define LOCAL = 1;
using namespace std;
typedef long long ll;
const int inf = 1e9 + ;
const ll mod = 1e9 + ;
const int Max = 4e4 + ; int n;
int val[], len;
int sum; void combine(int k)
{
int add = val[k] + val[k - ]; //合并
sum += add; //累加合并的值 for (int i = k; i < len; i++) //k之后的数前移一个位置
{
val[i] = val[i + ];
}
len--; //合并后,总长度减一
int pos = k - ;
while (pos > && val[pos - ] < add) //找到k前的第一个大于add的数
{
val[pos] = val[pos - ];
pos--;
}
val[pos] = add; //插入第一个比add大的数后面
while (pos >= && val[pos] >= val[pos - ])
{
int res = len - pos; //记录pos之后的元素个数,相当于跳过了,后面要补上
combine(pos - ); //合并
pos = len - res; //后面那一段仍然跳过
}
} int main()
{
scanf("%d", &n);
for (int i = ; i < n; i++)
{
scanf("%d", val + i);
}
len = ; //跳过第一个
sum = ;
for (int i = ; i < n; i++)
{
val[len++] = val[i]; //将值加在尾部
while (len >= && val[len - ] < val[len - ])
{
combine(len - );
}
}
while (len > )
combine(len - );
printf("%d\n", sum);
return ;
}

石子合并-环形版

(点击此处查看例题)

相比于直线版的,环形版可以从每一个点j开始,合并其后i堆石子为一堆,1 <= i <= n-1 ,合并的区间长度不受j的影响,而直线版的i满足: 1 <= i <= n - j ,合并的区间收到j的影响,那么我们就将整个环拆成n段链,求出合并这n段链中得分最大(小)者即可,其余操作和直线版一样

朴素写法

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#include<stack>
#include <map>
#include <iomanip> #define bug cout << "**********" << endl
#define show(x, y) cout<<"["<<x<<","<<y<<"] "
#define LOCAL = 1;
using namespace std;
typedef long long ll;
const int inf = 1e7 + ;
const ll mod = 1e9 + ;
const int Max = 2e2 + ; int n;
int a[];
int sum[];
int dp1[][], dp2[][]; int main()
{
#ifdef LOCAL
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
scanf("%d", &n);
for (int i = ; i <= * n; i++)
{
if (i <= n)
scanf("%d", a + i), a[i + n] = a[i]; sum[i] = sum[i - ] + a[i];
}
for (int i = ; i < n; i++)
{
for (int j = ; j + i <= * n; j++)
{
int e = i + j;
dp1[j][e] = inf;
dp2[j][e] = -;
for (int k = j; k + <= e; k++)
{
dp1[j][e] = min(dp1[j][e], dp1[j][k] + dp1[k + ][e] + sum[e] - sum[j - ]);
dp2[j][e] = max(dp2[j][e], dp2[j][k] + dp2[k + ][e] + sum[e] - sum[j - ]);
}
}
}
int min_val = inf, max_val = ;
for (int i = ; i <= n; i++)
{
min_val = min(min_val, dp1[i][i + n - ]);
max_val = max(max_val, dp2[i][i + n - ]);
}
printf("%d\n%d\n", min_val, max_val);
return ;
}

四边形优化写法(目前只能求最小代价)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#include<stack>
#include <map>
#include <iomanip> #define bug cout << "**********" << endl
#define show(x, y) cout<<"["<<x<<","<<y<<"] "
#define LOCAL = 1;
using namespace std;
typedef long long ll;
const int inf = 1e7 + ;
const ll mod = 1e9 + ;
const int Max = 1e3 + ; int n;
int a[Max<<];
int sum[Max];
int dp[Max][Max],s[Max][Max]; int main()
{
#ifdef LOCAL
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
scanf("%d", &n);
for (int i = ; i <= * n; i++)
{
if (i <= n)
scanf("%d", a + i), a[i + n] = a[i]; sum[i] = sum[i - ] + a[i];
s[i][i] = i;
}
for (int i = ; i < n; i++)
{
for (int j = ; j + i <= * n; j++)
{
int e = i + j;
dp[j][e] = inf;
for (int k = s[j][e-]; k <= s[j+][e]; k++)
{
if(dp[j][e] > dp[j][k] + dp[k + ][e] + sum[e] - sum[j - ])
{
dp[j][e] = dp[j][k] + dp[k + ][e] + sum[e] - sum[j - ];
s[j][e] = k;
}
}
}
}
int min_val = inf;
for (int i = ; i <= n; i++)
{
min_val = min(min_val, dp[i][i + n - ]);
}
printf("%d\n", min_val);
return ;
}

石子合并(直线版+环形版)&(朴素写法+四边形优化+GarsiaWachs算法)的更多相关文章

  1. 石子合并2(环形求最优解 区间dp)

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

  2. 石子合并(四边形不等式优化dp) POJ1160

    该来的总是要来的———————— 经典问题,石子合并. 对于 f[i][j]= min{f[i][k]+f[k+1][j]+w[i][j]} From 黑书 凸四边形不等式:w[a][c]+w[b][ ...

  3. HRBUST - 1819 石子合并问题--圆形版(区间dp+环形+四边形优化)

    石子合并问题--圆形版 在圆形操场上摆放着一行共n堆的石子.现要将石子有序地合并成一堆.规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆石子数记为该次合并的得分.请编辑计算出将n堆石子合并成一堆的 ...

  4. HRBUST 1818 石子合并问题--直线版

    石子合并问题--直线版 Time Limit: 1000ms Memory Limit: 32768KB This problem will be judged on HRBUST. Original ...

  5. HRBUST 1819 石子合并问题--圆形版

    石子合并问题--圆形版 Time Limit: 1000ms Memory Limit: 32768KB This problem will be judged on HRBUST. Original ...

  6. HDU 3506 (环形石子合并)区间dp+四边形优化

    Monkey Party Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)Tot ...

  7. RQNOJ 490 环形石子合并

    题目链接:https://www.rqnoj.cn/problem/490 题目描述 在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一 ...

  8. 洛谷P1880 石子合并(环形石子合并 区间DP)

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

  9. HDU3506环形石子合并问题

    HDU3506环形石子合并问题 线性的石子合并问题比较好理解,环形的转成线性的方法就是扩展数组 1 2 3 . . . n 1 2 3 ... n 依据是我们最优的取值可以是 1 --- n也能是 2 ...

随机推荐

  1. ios端,input框,汉字输入不上问题

    input{ -webkit-transform: translate3d(, , ); } 在input框上加上这段代码就可以了 另外,我在一个页面上,用一个开关去控制一部分内容显示隐藏与显示时,当 ...

  2. SpringMVC——文件上传下载

    一.单文件上传 1.导入依赖 <dependency> <groupId>commons-io</groupId> <artifactId>common ...

  3. 2016"百度之星" - 初赛(Astar Round2A)1006 Gym Class(HDU5695)——贪心+拓扑排序

    分析:首先,利用贪心可知,如果要所有人的分数和最高,需要把序号大的优先放在前面.其次,对于a的前面不能为b,那么只能a在b前面了,那么就建立一条从a到b的边,并且b的入度加1.然后就是拓扑排序了.要分 ...

  4. java虚拟机JVM

    Java Virtual Machine ,简称JVM; 它是运行所有Java程序的抽象计算机,是Java语言的运行环境,它是Java 最具吸引力的特性之一,JVM读取并处理编译过的与平台无关的字节码 ...

  5. 4.rabbitmq--路由模式

    rabbitmq--路由模式 想象之前的订阅发布模式: 一个生产者,多个消费者,每一个消费者都有自己的一个队列,生产者没有将消息直接发送到队列,而是发送到了交换机,每个队列绑定交换机,生产者发送的消息 ...

  6. display:flex 布局详解(2)

    1.  flex设置元素垂直居中对齐 在之前的一篇文章中记载过如何垂直居中对齐,方法有很多,但是在学习了flex布局之后,垂直居中更加容易实现 HTML代码: <div class=" ...

  7. python数据挖掘决策树算法

    决策树是一个非参数的监督式学习方法,主要用于分类和回归.算法的目标是通过推断数据特征,学习决策规则从而创建一个预测目标变量的模型.如下如所示,决策树通过一系列if-then-else 决策规则 近似估 ...

  8. 小D课堂 - 新版本微服务springcloud+Docker教程_5-08 断路器监控仪表参数

    笔记 8.断路器监控仪表参数讲解和模拟熔断     简介:讲解 断路器监控仪表盘参数和模拟熔断 1.sse  server-send-event推送到前端 资料:https://github.com/ ...

  9. tensorflow安装过程-(windows环境下)---详解

    一, 前言:本次安装tensorflow是基于Python的,安装Python的过程不做说明(既然决定按,Python肯定要先了解啊):本次教程是windows下Anaconda安装Tensorflo ...

  10. React Native小知识点记录

    1>查看 RN 的所有历史版本: npm view react-native versions -json 2>查看 RN 的当前版本: npm view react-native ver ...