石子合并问题 GarsiaWachs算法
目录引入
一个较为朴素的算法
GarsiaWachs算法
引入
在一个操场上摆放着一排 \(N\) 堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的 \(2\) 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。
试设计一个算法,计算出将 \(N\) 堆石子合并成一堆的最小得分。
数据范围 \(n \le 4e4\)
一个较为朴素的算法
不是暴力
按照区间DP的思路来做
设 \(f_{i, j}\) 表示区间 \([i, j]\) 合并的最小得分,枚举一个端点 \(k\),那么有转移方程:(其中 \(a\) 数组是预处理后的前缀和)
\[f_{i, j} = \min \{ f_{i, j}, f_{i, k} + f_{k + 1, j} + a_j - a_{i - 1} \}
\]
注意 \(i\) 要倒序枚举,\(j\) 要正序枚举
答案就是 \(f_{1, n}\)
时间复杂度: \(O(n^3)\);空间复杂度:\(O(n^2)\)
Code
/*
Work by: Suzt_ilymics
Knowledge:
Time: O()
*/
#includeiostream
#includecstdio
#includecstring
#includealgorithm
#define LL long long
#define orz cout"lkp AK IOI!"endl
using namespace std;
const int MAXN = 22335;
const int INF = 1e9+7;
const int mod = 1e9+7;
int n;
int a[MAXN];
int f[MAXN][MAXN];
int read(){
int s = 0, f = 0;
char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s 1) + (s 3) + ch - '0' , ch = getchar();
return f -s : s;
}
int main()
{
n = read();
for(int i = 1; i = n; ++i) a[i] = read() + a[i - 1];
for(int i = n; i = 1; --i){
for(int j = i; j = n; ++j){
if(i == j) continue;
f[i][j] = INF;
for(int k = i; k j; ++k) f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + a[j] - a[i - 1]);
}
}
printf("%d\n", f[1][n]);
return 0;
}
GarsiaWachs算法
发现数据范围太大了,上面的算法已经不能满足我们的需求,这里有一种优化算法,专门用来解决石子问题
每次操作,从前向后找一个最小的 \(k\),使其满足 \(a_{k - 1} \le a_{k + 1}\),然后合并 \(a_{k - 1}\) 和 \(a_k\)
从 \(k\) 开始向前找到第一个 \(j\) 使得 \(a_j a_{k - 1} + a_k\),并将合并后的新值插入位置 \(j\) 后面
进行 \(n - 1\) 次结束,在合并过程中统计答案即可
时间复杂度:\(O(n^2)\);空间复杂度:\(O(n)\)
正确性证明:作为一个OI,会应用就好啦,其实是我不会
Code
/*
Work by: Suzt_ilymics
Knowledge:
Time: O()
*/
#includeiostream
#includecstdio
#includecstring
#includealgorithm
#includevector
#define LL long long
#define orz cout"lkp AK IOI!"endl
using namespace std;
const int MAXN = 1e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;
LL n, ans = 0;
vectorint a;
int read(){
int s = 0, f = 0;
char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s 1) + (s 3) + ch - '0' , ch = getchar();
return f -s : s;
}
int merge(){//GarsiaWachs算法
int k = a.size() - 2;
for(int i = 0; i a.size() - 2; ++i)
if(a[i] = a[i + 2]){
k = i; break;
}
int sum = a[k] + a[k + 1];
a.erase(a.begin() + k);
a.erase(a.begin() + k);
int inst = -1;
for(int i = k - 1; i = 0; --i)
if(a[i] sum){
inst = i; break;
}
a.insert(a.begin() + inst + 1, sum);
return sum;
}
int main()
{
n = read();
for(int i = 1; i = n; ++i) a.push_back(read());
for(int i = 1; i n; ++i) ans += merge();
printf("%lld", ans);
return 0;
}
石子合并问题 GarsiaWachs算法 相关文章
图解算法——合并k个排序列表(Merge k Sorted Lists)
1. 题目描述 You are given an array of k linked-lists lists, each linked-list is sorted in ascending order. Merge all the linked-lists into one sorted linked-list and return it. 翻译: 给定一个 链表长度为k 的链表 数组 ,每个链表按 升序 排序
three.js中,绑定 mouseup 和 mousedown 事件不起作用/使用three.js实现点谁谁变红,已解决鼠标选不到目标问题
先检查一下你是否使用了 OrbitControls 如果使用了,同时想要添加点击事件,给物体模型添加点击事件,例如添加点到谁,谁就红 代码如下: // 点击功能let raycaster = nulllet instersected = nulllet mouse = nulllet useClick = false/** * 打开addClick函
第39天学习打卡(多线程 Thread Runnable 初始并发问题 Callable )
多线程详解 01线程简介 Process与Thread 程序:是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。 进程则是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位。 通常在一个进程中可以包含若干个线程,当然一个进程中
Ubuntu下SystemTap的安装问题解决记录
SystemTap的安装方式: 一.直接通过apt-get安装 sudo apt-get install systemtap 二.通过安装包下载 $ sudo apt-get install build-essential$ sudo apt-get install gettext$ sudo apt-get install elfutils$ sudo apt-get install libdw-dev$ wget https:/
【剑指Offer-15】二进制中1的个数
问题 请实现一个函数,输入一个整数(以二进制串形式),输出该数二进制表示中 1 的个数。例如,把 9表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。 示例 输入: 00000000000000000000000000001011 输出: 3 解释: 输入的二进制串
C++求快速幂
“快速幂”被归结为一个a的b次方对m取余的问题,即 a b % m 问题的关键在于怎么更快地求得a b ,直观的做法是用b次循环去累乘a,时间复杂度是O(b)。而“快速幂”,又成为“二分幂”,通过二分的思想能在O(log b )的复杂度内求得a b 。 如果次数b是奇数,则a
01背包问题理解动态规划算法
一.动态规划算法 简单理解:在一些分治算法解决的问题中,需要将较大规模的问题转化为较小规模的问题,往往会用到递归。但是在一些问题中,递归的小问题被多次重复运算,浪费了性能,因此可以使用数组或者其他合适的方式将运算过的小规模问题的结果记录下来
使用Python批量合并文件夹下.csv数据
实现目标:一个文件夹下包括n个.csv数据文件,想将后缀为ts.csv的文件与对应数字的.csv文件进行合并 此图为wf.csv文件中的数据格式,ts.csv文件是与此文件等行数的一列数据,将此列数据添加到已有18列数据的后面,完成数据合并操作 代码如下: import pandas
【剑指Offer-13】机器人的运动范围
问题 地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格
类似jar文件使用java无法打开问题
## 原因我一个彩笔在ctfhub,想对图片进行分割,拼接等操作,需要用到**Stegsolve**但是安装了java环境的我无法打开,经过校友的3、4次卸载安装还是没有让这个文件打开。。。(当然选择了正确的应用程序打开方式,环境变量也试了,就是打不开)命令窗口输入j