石子合并问题总结

C++动态规划经典案例解析之合并石子 区间类型问题,指求一个数列中某一段区间的值,包括求和、最值等简单或复杂问题。此类问题也适用于动态规划思想。如前缀和就是极简单的区间问题。现给定区间信息[3,6],求区间内所有数字相加结果。即求如下图位置数字之和。区间至少包括2个属性,起始端和结束端,求和范围包含左端和右端数字。累加数组中0~6区间的值s1。累加数组中0~2区间的值s2。将s1中的值减去s2中的值。得到最终结果。如果对任意区间的求解要求较频繁,会存在大量的重复计算。如分别求区间[2,5]和[1,5]之和时,分析可知区间。 阅读详情

直线石子合并

题目链接

时间复杂度: O(n3) O ( n 3 ) 。
空间复杂度: O(n2) O ( n 2 ) 。

∀k∈[l,r),dp[l][r]=mindp[l][k]+dp[k+1][r]+∑i=lra[i] ∀ k ∈ [ l , r ) , d p [ l ] [ r ] = min d p [ l ] [ k ] + d p [ k + 1 ] [ r ] + ∑ i = l r a [ i ]
, 为合并区间 [l,r] [ l , r ] 的最小合并代价。

代码:

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 1e3+7;
int dp[maxn][maxn];
int sum[maxn];
int a[maxn];
int inf = 0x3f3f3f3f;
int d(int l,int r){
    if(dp[l][r] != inf) return dp[l][r];
    for(int k = l;k<r;k++){
        dp[l][r] = min(dp[l][r],d(l,k)+d(k+1,r)+sum[r]-sum[l-1]);
    }
    return dp[l][r];
}
int main(){
    ios::sync_with_stdio(0);
    int n;cin >> n;
    for(int i = 1;i<=n;i++){
        cin >> a[i];
        sum[i]+=a[i]+sum[i-1];
    }
    memset(dp,0x3f,sizeof(dp));
    for(int i = 0;i<=n;i++)
        dp[i][i] = 0;
    cout << d(1,n) << endl;
}

圆形石子合并

n∼ 102 n ∼   10 2

题目链接
注意:此题是多组输入。

解法1

时间复杂度: O(n3) O ( n 3 ) 。
空间复杂度: O(n2) O ( n 2 ) 。

dp方程为

∀k∈[l,r), dp[l][r]=dp[l][k]+∑i=lra[i], when l≤r ∀ k ∈ [ l , r ) ,   d p [ l ] [ r ] = d p [ l ] [ k ] + ∑ i = l r a [ i ] ,   when  l ≤ r
以及
∀k∈[1,r)∪[l,n], dp[l][r]+∑i=lna[i]+∑i=1ra[i], when l>r ∀ k ∈ [ 1 , r ) ∪ [ l , n ] ,   d p [ l ] [ r ] + ∑ i = l n a [ i ] + ∑ i = 1 r a [ i ] ,   when l>r
。

初始条件:

dp[i][j]=0,when i=j,dp[i][j]=∞,otherwise. d p [ i ] [ j ] = 0 , when  i = j , d p [ i ] [ j ] = ∞ , otherwise.

递归
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 1e2+7;
int dp[maxn][maxn];
int sum[maxn];
int a[maxn];
int inf = 0x3f3f3f3f;
int n;
int d(int l,int r){
    if(l*r == 0) return inf;
    if(dp[l][r] < inf) return dp[l][r];
    if(l<=r)
        for(int k = l;k<r;k++){
            dp[l][r] = min(dp[l][r],d(l,k)+d(k+1,r)+sum[r]-sum[l-1]);
        }
    else{
        for(int k = l;k<r || k>=l;k=k%n+1){
            dp[l][r] = min(dp[l][r],d(l,k)+d((k)%(n)+1,r)+sum[n]-sum[l-1]+sum[r]);
        }
    }
    return dp[l][r];
}
int main(){
    ios::sync_with_stdio(0);
    while(cin >> n){
        memset(dp,0x3f,sizeof(dp));
        memset(sum,0,sizeof(sum));
        for(int i = 0;i<=n;i++)
            dp[i][i] = 0;
        for(int i = 1;i<=n;i++){
            cin >> a[i];
            sum[i]=a[i]+sum[i-1];
        }
        if(n == 1) {
            cout << a[1] << endl;
            continue;
        }
        int ans = inf;
        for(int i = 1;i<=n;i++)
            ans = min(ans,d(i,(i+n-1)%n));
        cout << ans << endl;
    }
}

解法2

将石子堆复制一份,用直线情形解决。

注意数组大小。

迭代
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 2e2+7;
int dp[maxn][maxn], sum[maxn];
int n;
int main(){
    while(~scanf("%d",&n)){
        memset(dp,0x1f,sizeof(dp));
        for(int i = 0;i<=2*n;i++){
            dp[i][i] = 0;
        }
        for(int i = 1;i<=n;i++){
            scanf("%d",&sum[i]);
            sum[i+n] = sum[i];
        }
        for(int i = 1;i<=2*n;i++){
            sum[i]+=sum[i-1];
        }
        for(int d = 1;d<n;d++){
            for(int i = 1;i<=2*n-d;i++){
                int j = i+d;
                for(int k = i;k<j;k++){
                    dp[i][j] = min(dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1],dp[i][j]);
                }
            }
        }
        int ans = 0x1f1f1f1f;
        for(int i = 1;i<=n;i++){
            int j = i + n - 1;
            ans = min(ans,dp[i][j]);
        }
        printf("%d\n",ans);
    }
}
递归

这里略去……因为差不多。

四边形不等式优化

时间复杂度: O(n2) O ( n 2 ) 。
空间复杂度同

直线石子合并

迭代

鸽了

环形石子合并

题目链接

n∼103 n ∼ 10 3

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 2e3+7;
int dp[maxn][maxn], sum[maxn], s[maxn][maxn];
int n;
int main(){
    scanf("%d",&n);
    memset(dp,0x1f,sizeof(dp));
    for(int i = 0;i<=2*n;i++){
        dp[i][i] = 0;
        s[i][i] = i;
    }
    for(int i = 1;i<=n;i++){
        scanf("%d",&sum[i]);
        sum[i+n] = sum[i];
    }
    for(int i = 1;i<=2*n;i++){
        sum[i]+=sum[i-1];
    }
    for(int d = 1;d<n;d++){
        for(int i = 1;i<=2*n-d;i++){
            int j = i+d;
            int &temp = dp[i][j];
            int &ts = s[i][j];
            for(int k = s[i][j-1];k<=s[i+1][j];k++){
                int orz = dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
                if(temp > orz){
                    temp = orz;
                    ts = k;
                }
            }
        }
    }
    int ans = 0x1f1f1f1f;
    for(int i = 1;i<=n;i++){
        int j = i + n - 1;
        ans = min(ans,dp[i][j]);
    }
    printf("%d\n",ans);
}

GarsiaWachs算法

题目链接

n∼5×104 n ∼ 5 × 10 4

时间复杂度: O(n2) O ( n 2 ) ,若使用平衡树维护 O(nlogn) O ( n log ⁡ n ) 。
空间复杂度: O(n) O ( n ) 。

因为这个算法不是那么直观,所以我多写一点。

从朴素贪心开始

考虑三块连续的石堆 [a,b,c] [ a , b , c ] ,计算两种合并方法的代价。

s1s2=(a+b)+((a+b)+c)=2a+2b+c=(a+(b+c))+(b+c)=a+2b+2c s 1 = ( a + b ) + ( ( a + b ) + c ) = 2 a + 2 b + c s 2 = ( a + ( b + c ) ) + ( b + c ) = a + 2 b + 2 c

注意到,若 a≤c a ≤ c ,就有 s1≤s2 s 1 ≤ s 2 。所以,三个连续石堆的合并方法,只与 a a 和c的大小关系有关。

所以我们就能得到一个朴素的贪心算法,即:
不断地从左往右遍历,找到形似 [a,b,c] [ a , b , c ] 且 a≤c a ≤ c 的三个连续石堆,就合并 a a 和b。

但是朴素贪心算法是错误的。
反例: [6,4,2,1,3,5,7] [ 6 , 4 , 2 , 1 , 3 , 5 , 7 ] 。最小代价是74,贪心结果是76。

形式证明鸽了。

Garisia-Wachs的改进

  • 设石堆数组为 stone s t o n e ,下标从0开始。其中每个元素代表此石堆的石子数量。
  • 约定 stone[0]=stone[len]=+∞ s t o n e [ 0 ] = s t o n e [ l e n ] = + ∞ , ans=0 a n s = 0 。
  • 此数组长度记为 |stone|=len | s t o n e | = l e n ,计入两端。也就是说 [+∞,1,+∞] [ + ∞ , 1 , + ∞ ] 长度为3。同时,因为至少有一个石堆,3也是 stone s t o n e 数组的最短可能长度。
  • 数组的下标较小的一端为左端。

具体算法如下:
1. 依次令 i=1…len−2 i = 1 … l e n − 2 ,直到有 stone[i]≤stone[i+2] s t o n e [ i ] ≤ s t o n e [ i + 2 ] 。(肯定会出现的,想想为啥)
2. 令 temp=stone[i]+stone[i+1] t e m p = s t o n e [ i ] + s t o n e [ i + 1 ] 。
3. 向左寻找,找到 j j 满足j<i且 stone[j]>temp s t o n e [ j ] > t e m p 。
4.删除 stone[i+1] s t o n e [ i + 1 ] 这个元素(右方元素依次左移),并将 temp t e m p 插入到 stone[j] s t o n e [ j ] 的右边(右方元素依次右移)。本次合并代价为 temp t e m p ,累加入 ans a n s 。
5.反复执行此过程,直到 stone s t o n e 数组内只有一个非无穷元素(只剩一个石堆 ),此时 ans a n s 的值即是最小合并代价。

正确性证明……鸽了。

vector朴素实现

用stl的vector实现。用时接近2s。
注意答案用long long。

#include<bits/stdc++.h>
#define LL long long
#define sz(v) int(v.size())
using namespace std;
const int maxn = 5e4+7;
const LL inf = 0x3f3f3f3f3f3f3f3f;
int n,x;
int main(){
    scanf("%d",&n);
    vector<LL> v;
    v.push_back(inf);
    for(int i = 1;i<=n;i++){
        scanf("%d",&x);
        v.push_back(x);
    }
    v.push_back(inf);

    LL ans = 0;
    for(int i = 1;i+2<sz(v);i++){
        if(v[i]<=v[i+2]){
            v[i] += v[i+1];
            LL temp = v[i];
            ans += temp;
            v.erase(v.begin()+i+1); //删除v[i+1]这个元素
            for(int j = i;j>=1;j--){
                if(v[j-1]>temp){ //往回找到合适的位置
                    v.erase(v.begin()+i);//删除v[i]这个元素
                    v.insert(v.begin()+j,temp);//插入v[i]到v[j]之后
                    break;
                }
            }
            i = 0;
        }
    }
    printf("%lld\n",ans);
}

值得注意的是,vector的insert方法在传入的位置之前(左方)插入元素。

有点厉害的实现

265ms……很强
51nod上扒的。

#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
#define MAXN 50005
#define LL long long
using namespace std;
int n, num;
LL ans;
int data[MAXN];
void dfs(int now)
{
    int j;
    int temp = data[now - 1] + data[now];//代价
    ans += (LL)temp;
    for(int i = now; i < num - 1; i++) data[i] = data[i + 1];
    num--;
    for(j = now - 1; j > 0 && data[j - 1] < temp; j--) data[j] = data[j - 1];
    data[j] = temp;
    while(j >= 2 && data[j - 2] <= data[j])
    {
        int d = num - j;
        dfs(j - 1);
        j = num - d;
    }
}
int main()
{
    scanf("%d", &n);
    for(int i = 0; i < n; i++) scanf("%d", &data[i]);
    num = 1;
    ans = 0;
    for(int i = 1; i < n; i++)
    {
        data[num++] = data[i];
        while(num>=3 && data[num-3]<=data[num-1]) dfs(num - 2);
    }
    while(num > 1) dfs(num - 1);
    printf("%lld\n", ans);

    return 0;
}
C51编程25-应用篇(单片机与电脑端实现wifi通讯) 单片机使用ESP-01S wifi模块与电脑端进行数据通讯。 阅读详情

相关推荐

GNSS/INS松组合导航实战:如何用卡尔曼滤波提升定位精度(附MATLAB代码)

本文深入探讨了GNSS/INS松组合导航的工程实践,详细阐述了如何利用卡尔曼滤波算法融合卫星与惯性数据以提升定位精度。内容涵盖从仿真环境搭建、误差状态卡尔曼滤波器(ESKF)设计,到状态与观测方程实现、噪声参数调试及常见问题排查的全过程,并提供了可直接运行的MATLAB代码,为开发高精度、鲁棒的组合导航系统提供实战指南。

weixin_29057163的博客 165

【算法设计与分析】动态规划解决石子合并问题(课程设计)

动态规划算法是计算机算法设计中的一个重要算法,通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推的方式去解决,石子合并问题作为典型算例,能够彰显动态规划算法特征 针对石子合并问题,本文利用动态规划算法寻求石子合并时的最大,最小得分,选择相邻的两堆石子堆进行合并,其最终花费的代价与石子堆的排列顺序有关。根据其重叠子问题建立状态转移方程,利用程序进行求解。算例结果显示:将4堆石子合并成一堆,每堆的石子个数分别是4,4,5,9,合并的代价最小得分为43,最大得分为54

qq_44871101的博客 1万+

abaqus 根据坐标施加载荷- Analytical Field 载荷映射

在一些类型的分析中,需要将按位置分布的载荷施加到结构上,比如流体分析导出了结构的气动载荷、或者温度载荷,需要将压强分布、温度分布载荷施加到固体上。这种分析可以使用abaqus中的Analytical Field实现。 1 例子 1.1 结构 一个100*100mm的面,面的载荷分布函数为p=(x2+y2)0.5/((20.5)*100) MPa,即(0, 0)的压强为0,(100, 100)的压强为1。 1.2 载荷 载荷函数:p=(x^2+y^2)^0.5/((2^0.5)*100) in exce.

shengyutou的博客 2万+

石子合并 区间dp

前缀和、区间dp

WananRd 1241

物理小白也能懂的PINN指南:用Python给神经网络注入‘物理常识‘

本文为物理小白提供了一份通俗易懂的PINN入门指南。通过生活化类比和Python实战,详细讲解了如何将物理定律作为约束注入神经网络,使其具备“物理常识”,从而在数据稀缺时也能做出符合规律的预测。文章以热传导方程为例,手把手教你从零构建物理信息神经网络。

open4的博客 349

2 利用ITK实现多帧Dicom读取,VTK实现数据显示

vtk中也有dicom读写类,但是只能完成单帧DICOM数据读取,这对于实际应用是非常不利的。而ITK可以实现dicom多帧读取,通常情况ITK与VTK也会结合使用(ITK处理,VTK显示),所以就一起学习下吧!

buerbuhuitou的博客 1564

石子合并(GarsiaWachs算法)

对于石子合并问题,有一个最好的算法,那就是GarsiaWachs算法。时间复杂度为O(n^2)。 它的步骤如下: 设序列是stone[],从左往右,找一个满足stone[k-1] stone[k+1]的k,找到后合并stone[k]和stone[k-1],再从当前位置开始向左找最大的j,使其满足stone[j] > stone[k]+stone[k-1],插到j的后面就行。一直

ACdreamer 1万+

彻底讲透 SAP 中的 client-safe:从 CDS、AMDP 到 ABAP Cloud 的客户端隔离设计

SAP系统中的client-safe概念定义了技术边界:访问client-dependent数据的对象必须确保只看到单个client数据,而访问client-independent数据的对象则天然满足条件。这一原则直接影响CDS视图、AMDP等核心组件,并与ABAP Cloud开发模型紧密集成。在业务系统中,client isolation要求用户只能看到当前登录client的数据。SAP通过语言版本、对象规则和静态检查强制执行这一约束,特别是在ABAP Cloud开发中,AMDP方法和发布的API必须满足

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 3267

Acwing 0x50 动态规划~石子合并问题及复杂度的多种不同优化探讨

本文章会探讨线性石子合并问题及其复杂度优化问题,环形石子合并问题,会持续更新。咕咕咕~ 线性石子合并 题目链接 (NOI1995)https://www.acwing.com/problem/content/284/ 题意 设有堆石子排成一排,其编号为。 每堆石子有一定的质量,可以用一个整数来描述,现在要将这堆石子合并成为一堆。 每次只能合并相邻的两堆,合并的代价为这两堆石...

- 954

PCIe Base Specification解析(二)

PCIe事务层,Transaction Layer Specification

Crazzy_M的博客 1292

计算机视觉(角点检测)- 1 - Harris角点检测

计算机视觉(角点检测)-1.1 Harris角点检测学习前言 学习前言   今天更新一下计算机视觉的东西吧,哈哈哈o( ̄︶ ̄)o   关于角点检测呢,就是检测图像中角点的算法,常见的有四种常见的算法:Moravec角点检测算法、 Harris角点检测算法、Shi-Tomasi 算法、FAST角点检测算法,不过常用的就两种吧——Harris角点检测算法、Shi-Tomasi 算法(反正我基本上就只用到这两种,先着重介绍前面两种,后面再花世界介绍吧)(~ ̄▽ ̄)~ ...

qq_40374812的博客 1万+

石子合并系列问题

动态规划中的石子合并系列问题 设有N堆石子排成一排,其编号为 1,2,3,…,N。每堆石子有一定的质量,可以用一个整数来描述,现在要将这N堆石子合并成为一堆。每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同。找出一种合理的方法,使总的代价最小,输出最小代价。将N堆石子绕圆形操场摆放,现要将石子有次序地合并成一堆,规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数记为该次合并的得分。

Hoper-J的博客 6025

SAP S4 HANA:按单生产(MTO)场景中的COGS分割

本文介绍了SAP S/4HANA中提供的各种选项,以在销售订单/商品是按订单生产(以下称为MTO)生产流程中的成本对象时,在基于帐户的COPA环境中报告COGS分割信息。

adam915的博客 1191

动态规划——石子合并问题

但不同的是,动态规划是自底向上分解,并且会保存子问题的解,在需要时可直接拿过来使用,这一点是区别于分治的。这类问题存在大量重叠子问题,即子问题的解可以被多次重复利用,而这些子问题的结果依赖于更小的子问题结果。子问题划分: 为了找到最优的合并顺序,我们需要考虑如何将区间 [i, j] 划分为更小的子区间 [i, k] 和 [k+1, j],并递归地计算它们的最优解。例如,如果我们有三堆石子 [4, 4, 5],不同的合并顺序会导致不同的得分,总共有两种合并顺序,分别为21和22。

有时候,想要一块二向箔 3956

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

石子合并问题(动态规划)

动态规划 石子合并问题

m0_52124992的博客 2万+

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

石子合并问题

定义 dp[i][j] 表示将第 i 堆到第 j 堆石子合并成一堆的最小合并代价。

2501_90941066的博客 995

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

dp算法 - 石子合并问题

这两天看了一下这个问题,原题是这样的: 有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动相邻的2堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费最小(或最大)。 这个题目在第一次刚看的时候,一脸懵逼,题目看懂了,但是怎么去求花费怎么都没有想明白,后来在网上看了一下这个算法的别人的理解,才看明白。 大概意思如下:如果有三堆:4,5,6。 第一种方案...

sunhengzhi_212的专栏 1万+

dp 石子合并问题

石子合并问题: 有n(N 两堆价值之和,求最终将所有的石子合并成一堆之后的代价最小值。 问题一:n堆石子排成一条直线 这个问题比较简单,类似与矩阵连乘的问题。 dp[n][m] 为合并n和m之间的石子的代价 则有dp[i][j] = 0 , i==j dp[n][m] = min(dp[n][k]+dp[k+1][m]) + sum(n,m)  n #include

liuyanfeier的专栏 623

(经典问题)—石子合并问题

题目链接:https://www.nowcoder.com/questionTerminal/6d3ccbc5b6ad4f12b8fe4c97eaf969e0 n堆石子摆在一排,每次合成相邻的两堆石子,合成的代价是两堆石子的数量之和。求出代价最少的方案合成所有石子直到只有一堆。 这是动态规划中的经典题目,官话就是找状态转移方程。 我在做动态规划的时候喜欢从后往前思考。 举一个例子,假设石堆...

ACM5100的博客 2180
上一篇: codeforces #462 D - A Twisty Movement - dp
下一篇: 解决Thinkpad x220 无法待机/无法更改bios设置的问题
lanadeus
博客等级 码龄9年 54粉丝 38原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值