当前位置: 首页 > news >正文

MIT-最大连续子序列和(MCS)

文章目录

  • 问题描述
  • 例子
  • 算法实现
    • 暴力算法
    • 重用数组
    • 分治算法(DC)
    • 动态规划解法

问题描述

设有一家公司的历年利润记录如下:

在连续的若干年中,每一年的利润(单位:百万美元)可表示为一个序列:

P=[−3,2,1,−4,5,2,−1,3,−1]P=[-3,2,1,-4,5,2,-1,3,-1]P=[3,2,1,4,5,2,1,3,1]

问题的目标是:在该利润序列中,寻找一个连续子序列(contiguous subsequence),使得该子序列中所有元素的和达到最大值。

我们定义输入是一组数组 A[1...N]A[1...N]A[1...N]V(i,j)V(i,j)V(i,j) 是区间 [i,j][i,j][i,j] 中所有元素的额和,其中 1≤i≤j≤n1\leq i\leq j\leq n1ijn ,有:

V(i,j)=∑x=ijA(x)V(i,j)=\sum_{x=i}^j A(x)V(i,j)=x=ijA(x)

我们的目的是找到一组索引 i≤ji\leq jij,使得:

∀(i′,j′),V(i′,j′)≤V(i,j)\forall(i^{\prime},j^{\prime}),V(i^{\prime},j^{\prime})\leq V(i,j)(i,j),V(i,j)V(i,j)

例子

在上述利润序列中,若选择第 5 年至第 8 年的利润(即 [5,8][5,8][5,8] 区间),对应的子序列为 [5,2,−1,3][5,2,−1,3][5,2,1,3]。其总利润为:

5+2−1+3=95+2-1+3=95+21+3=9

这是所有可能的连续年份区间中利润总和的最大值。因此,该公司在第 5 年至第 8 年期间取得了最高的累计利润 9 百万美元。

算法实现

暴力算法

首先我们想到的就是遍历所有可能的区间 (i,j)(i,j)(i,j),然后来计算每个区间的 V(i,j)V(i,j)V(i,j),从而找到最大的 V(i,j)V(i,j)V(i,j)

int V;
int VMAX = A[1]
for (int i = 1; i <= n; i ++ )
{for (int j = i; j <= n; j ++ ){V = 0;for (int k = i; k <= j; k ++ )V += A[k];if (V > VMAX)	VMAX = V;}
}
return VMAX;

时间复杂度:O(n3)O(n^3)O(n3)


重用数组

假设数组 A=[1,2,3,4,5]A=[1,2,3,4,5]A=[1,2,3,4,5]

第一个暴力算法:

  • 对于 V(1,3)=A[1]+A[2]+A[3]=6V(1,3)=A[1]+A[2]+A[3]=6V(1,3)=A[1]+A[2]+A[3]=6
  • 对于 V(1,4)=A[1]+A[2]+A[3]+A[4]=10V(1,4)=A[1]+A[2]+A[3]+A[4]=10V(1,4)=A[1]+A[2]+A[3]+A[4]=10

暴力算法重复计算了A[1],A[2],A[3]A[1],A[2],A[3]A[1],A[2],A[3]。而 V(1,3)=A[1]+A[2]+A[3]V(1,3)=A[1]+A[2]+A[3]V(1,3)=A[1]+A[2]+A[3],我们可以发现V(1,4)=V(1,3)+A[4]V(1,4)=V(1,3)+A[4]V(1,4)=V(1,3)+A[4]。这样不就可以大幅度的减少了计算量。

故此我们定义:

V(i,j)=∑x=ijA[x]=V(i,j−1)+A[j]V(i,j)=\sum_{x=i}^jA[x]=V(i,j-1)+A[j]V(i,j)=x=ijA[x]=V(i,j1)+A[j]

int VMAX = A[1];
int V;for (int i = 1; i <= n; i ++ )
{V = 0;for (int j = i; j <= n; j ++ ){V += A[j];if (V > VMAX) VMAX = V;}
}
return VMAX;

时间复杂度O(n2)O(n^2)O(n2)


分治算法(DC)

对于任意一个数组 A(i,j)A(i,j)A(i,j),如果我们将它从中间分成两半(中点是 M=⌊i+j2⌋M=\lfloor\frac{i+j}{2}\rfloorM=2i+j):

  • 左半部分:A(i,M)A(i,M)A(i,M)
  • 右半部分:A(M+1,j)A(M+1,j)A(M+1,j)

那么这个数组的最大连续子序列(MCS) 只可能存在于以下三种情况中:

  • S1S_1S1:MCS 完全位于左半部分。
  • S2S_2S2:MCS 完全位于右半部分。
  • AAA:MCS 跨越了中点。这个子序列一定是由 A(i,M)A(i,M)A(i,M)某个后缀(包含A[M]A[M]A[M])和 A[M+1,j]A[M+1,j]A[M+1,j]某个前缀(包含 A[M+1]A[M+1]A[M+1])拼接而成。

即最后的 最大连续子序列(MCS)max⁡(S1,S2,A)。\max(S_1,S_2,A)。max(S1,S2,A)

在这里插入图片描述
计算A的最大连续子序列(MCS):

在这里插入图片描述

int Find_A(int A[], int i, int j, int M)
{int A_1, A_2;int MAX = A[M];int SUM = 0;// 计算A(i,M)的最大连续子序列的某个后缀(包含A[M])for (int k = M; k >= i; k -- ){SUM += A[k];if (SUM > MAX) MAX = SUM;}A_1 = MAX;MAX = A[M + 1];SUM = 0;// 计算A(M + 1,j)的最大连续子序列的某个前缀(包含A[M + 1])for (int k = M + 1; k <= j; k ++ ){SUM += A[k];if (SUM > MAX) MAX = SUM;}A_2 = MAX;return A_1 + A_2;
}

划分与合并

int MCS(int A[], int i, int j)
{if (i == j) return A[i];int M = (i + j) / 2;int S_1 = MCS(A, i, M);int S_2 = MCS(A, M + 1, j);int A = Find_A(A, i, j, M);return MAX(S_1, S_2, A);
}

时间复杂度O(nlog⁡(n))O(n\log(n))O(nlog(n))

m=j−i+1m=j-i+1m=ji+1T(m)T(m)T(m) 是执行 MSC(A,i,j)MSC(A,i,j)MSC(A,i,j) 所需的时间。

  • if (i == j) return A[i]O(1)O(1)O(1)
  • int M = (i + j) / 2O(1)O(1)O(1)
  • int S_1 = MCS(A, i, M)O(m/2)O(m/2)O(m/2)
  • int S_2 = MCS(A, M + 1, j)O(m/2)O(m/2)O(m/2)
  • int A = Find_A(A, i, j, M)O(m)O(m)O(m)
  • return MAX(S_1, S_2, A)O(1)O(1)O(1)

前提条件:O(1)=T(1)O(1)=T(1)O(1)=T(1)nnn 是 2 的指数,且当 n>1n>1n>1 时有,Tn=2T(n2)+O(n)T_n=2T(\frac{n}{2})+O(n)Tn=2T(2n)+O(n)

我们一定可以找到一个常数 ccc ,使得 T(n)≤2T(n2)+cnT(n)\leq 2T(\frac{n}{2})+cnT(n)2T(2n)+cn

Tn≤2T(n2)+cn≤2[2T(n22)+cn2]+cn=22T(n22)+2cn≤22[2T(n23)+cn22]+2cn=23T(n23)+3cn≤...=2hT(n2h)+hcn\begin{aligned} T_n&\leq2T(\frac{n}{2})+cn \\&\leq 2\left[2T\left(\frac{n}{2^{2}}\right)+c\frac{n}{2}\right]+cn \\ & = 2^2T\left(\frac{n}{2^2}\right)+2cn \\ & \leq2^2\left[2T\left(\frac{n}{2^3}\right)+c\frac{n}{2^2}\right]+2cn \\ & = 2^3T\left(\frac{n}{2^3}\right)+3cn \\ & \leq ... \\& = 2^hT\left(\frac{n}{2^h}\right)+hcn \end{aligned}Tn2T(2n)+cn2[2T(22n)+c2n]+cn=22T(22n)+2cn22[2T(23n)+c22n]+2cn=23T(23n)+3cn...=2hT(2hn)+hcn

我们令 h=log⁡2nh=\log_2nh=log2n,所以 n=2hn=2^hn=2h,有:

T(n)≤nT(1)+cn(log⁡2n)=O(n)+cnlog⁡2n=O(nlog⁡2n)T(n)\leq nT(1) + cn(\log_2n)=O(n) + cn\log_2n=O(n\log_2n)T(n)nT(1)+cn(log2n)=O(n)+cnlog2n=O(nlog2n)


动态规划解法

  1. 定义状态

    • dp[i] 表示以第 i 个元素结尾的最大子序列和。
    • 目标是找到所有 dp[i] 的最大值,这个最大值即为整个数列中的最大连续子序列和。
  2. 状态转移

    • 对于每个元素 A[i],我们可以选择将它加入前面的子序列,或者从 A[i] 开始新的子序列。
    • 公式为:
      dp[i]=max⁡(dp[i−1]+A[i],A[i])dp[i]=\max(dp[i-1]+A[i],A[i])dp[i]=max(dp[i1]+A[i],A[i])
      这表示我们要么把 A[i] 加到之前的最大和(即继续前面的子序列),要么从 A[i] 开始一个新的子序列。

在这里插入图片描述

max_sum = A[1];
dp[1] = A[1];
for (int i = 2; i <= n; i ++ )
{dp[i] = max(dp[i - 1] + A[i], A[i]);max_sum = max(max_sum, dp[i])
}return max_sum;

时间复杂度:O(n)O(n)O(n)

http://www.dtcms.com/a/550163.html

相关文章:

  • 深圳市住建局网站官网济南网站建设公司哪家好
  • Kubernetes资源管理全解析
  • 郑州企业型网站建设怎么做可以访问网站
  • 网站制作前必须做的事情有哪些网站行业
  • TC3xx芯片ACCEN寄存器保护详解
  • Linux上如何挂载磁盘
  • 卫星轨道计算中的数值精度陷阱:第三体引力摄动的稳定性优化
  • 白山网站seoe网站的图标怎么做
  • RHSCA---用户和组管理
  • 温州网站域名注册服务公司易语言如何做浏网站
  • 仿糗事百科网站源码dede二次开发分享+评论+互动国外网站域名
  • 仓颉语言中String的内存表示深度解析
  • NetSuite 中自定义基础打印模板的调整方法分享
  • 东城企业网站开发什么网站能免费做简历
  • “调用销毁者置于末尾”原则
  • GRPO相关优化论文
  • Openvins学习---ov_msckf中的State.h
  • 有什么网站可以做数学题项目建设全过程
  • 德庆网站建设良精企业网站管理系统
  • dz网站自己做的模板放在哪里小说网站建设的支柱
  • idea中Docker一键自动化部署
  • faster-whisper热词详解与程序设计
  • 电子电力技术的控制电路学习分享1
  • 原生JS实现虚拟列表:从基础到性能优化的完整实践
  • 万全网站建设Myeclipse怎么做网站
  • C#: Contains 方法
  • .NET 泛型编程(泛型类、泛型方法、泛型接口、泛型委托、泛型约束)
  • 网站建设研究方法建筑图纸字母代表大全图解
  • PocketBase轻量级后端解决方案
  • 【JavaWeb|day19 Web后端进阶 SpringAOP、SpringBoot原理、自定义Starter、Maven高级】