algorithm - 找到分钟。 "join"序列操作

标签 algorithm sorting sequence

比方说,我们有一个正整数列表/数组 x1, x2, ... , xn。 我们可以对这个序列做一个join操作,这意味着我们可以用一个元素替换彼此相邻的两个元素,这是这些元素的总和。例如:

-> 数组/列表:[1;2;3;4;5;6]

  • 我们可以加入 2 和 3,并用 5 替换它们;
  • 我们可以加入5和6,并用11替换它们;
  • 我们不能 加入 2 和 4;
  • 我们不能 加入 1 和 3 等

主要问题是找到给定序列的最小连接操作,之后该序列将按升序排序。

注意:空序列和单元素序列按递增顺序排序。

基本示例:

  • 为 [4; 6; 5; 3; 9] 解决方案是 1(我们加入 5 和 3)

  • 对于 [1; 3; 6; 5]解也是1(我们加入6和5)

我正在寻找的是解决此问题的算法。它可以是伪代码、C、C++、PHP、OCaml 或类似语言(我的意思是:如果您使用其中一种语言编写解决方案,我会理解解决方案)。

最佳答案

这是使用动态规划解决的理想问题,@lijie 描述的递归正是正确的方法,只需进行一些小调整以确保考虑所有可能性。有两个关键的观察结果:(a) 任何连接操作序列都会产生原始向量的一组非重叠求和子序列,以及 (b) 对于最佳连接序列,如果我们看在任何求和子序列 (m...n) 的右侧,该部分是问题的最佳解决方案:“为子向量 (n+1)...N 找到最佳连接序列,使得结果最终序列被排序,所有元素 >= sum(m...n)。

直接实现递归当然会导致指数时间算法,但使用动态规划进行简单调整使其成为 O(N^2),因为基本上所有 (m,n) 对都被考虑一次。使用动态规划实现递归的一种简单方法是拥有一个由 (m,n) 索引的数据结构,一旦计算出 f(m,n) 的结果,就存储它们,以便下次我们调用 f(m ,n), 我们可以查找之前保存的结果。以下代码使用 R 编程语言执行此操作。我正在使用我们想要找到最小连接数以获得非递减序列的公式。对于 R 的新手,要测试此代码,只需从任意镜像(谷歌“R 项目”)下载 R,启动它,将两个函数定义(f 和 solve)粘贴到控制台,然后使用“solve(c(...))”求解任意向量在下面的示例中。

f <- function(m,n) {
  name <- paste(m,n)
  nCalls <<- nCalls + 1 
  # use <<- for global assignment
  if( !is.null( Saved[[ name ]] ) ) {
    # the solution for (m,n) has been cached, look it up
    nCached <<- nCached + 1
    return( Saved[[ name ]] )
  }
  N <- length(vec) # vec is global to this function
  sum.mn <- -Inf 
  if(m >= 1)
    sum.mn <- sum( vec[m:n] )
  if(n == N) { # boundary case: the (m,n) range includes the last number
    result <- list( num = 0, joins = list(), seq = c())
  } else
  {
    bestNum <- Inf
    bestJoins <- list()
    bestSeq <- c()
    for( k in (n+1):N ) {
      sum.nk <- sum( vec[ (n+1):k ] )
      if( sum.nk < sum.mn ) next
      joinRest <- f( n+1, k )
      numJoins <- joinRest$num + k-n-1
      if( numJoins < bestNum ) {
        bestNum <- numJoins
        if( k == n+1 )
          bestJoins <- joinRest$joins else
        bestJoins <- c( list(c(n+1,k)), joinRest$joins )
        bestSeq <- c( sum.nk, joinRest$seq)
      }
    }  
    result <- list( num = bestNum, joins = bestJoins, seq = bestSeq )
  }
  Saved[[ name ]] <<- result
  result
}

solve <- function(input) {
  vec <<- input
  nCalls <<- 0
  nCached <<- 0
  Saved <<- c()
  result <- f(0,0)
  cat( 'Num calls to f = ', nCalls, ', Cached = ', nCached, '\n')
  cat( 'Min joins = ', result$num, '\n')
  cat( 'Opt summed subsequences: ')
  cat( do.call( paste, 
                lapply(result$joins, 
                       function(pair) paste(pair[1], pair[2], sep=':' ))),
       '\n')
  cat( 'Final Sequence: ', result$seq, '\n' )
}

以下是一些示例运行:

> solve(c(2,8,2,2,9,12))
Num calls to f =  22 , Cached =  4 
Min joins =  2 
Opt summed subsequences: 2:3 4:5 
Final Sequence:  2 10 11 12 

> solve(c(1,1,1,1,1))
Num calls to f =  19 , Cached =  3 
Min joins =  0 
Opt summed subsequences:  
Final Sequence:  1 1 1 1 1 

> solve(c(4,3,10,11))
Num calls to f =  10 , Cached =  0 
Min joins =  1 
Opt summed subsequences: 1:2 
Final Sequence:  7 10 11 

> solve(c (2, 8, 2, 2, 8, 3, 8, 9, 9, 2, 9, 8, 8, 7, 4, 2, 7, 5, 9, 4, 6, 7, 4, 7, 3, 4, 7, 9, 1, 2, 5, 1, 8, 7, 3, 3, 6, 3, 8, 5, 6, 5))
Num calls to f =  3982 , Cached =  3225 
Min joins =  30 
Opt summed subsequences: 2:3 4:5 6:7 8:9 10:12 13:16 17:19 20:23 24:27 28:33 34:42 
Final Sequence:  2 10 10 11 18 19 21 21 21 21 26 46 

请注意,@kotlinski 考虑的序列的最小连接数是 30,而不是 32 或 33。

关于algorithm - 找到分钟。 "join"序列操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4486073/

相关文章:

java - Java中Trie数据结构空间使用

algorithm - 搜索的时间复杂度

algorithm - 最小特殊移动数排序数

sequence - Sybase序列

java - 如何从复杂的研究论文着手编写算法

python - 使用 numpy 数组通过索引加速获取边缘矩阵

python - 先按字母排序包含字母数字项目的列表

c - 使用结构对两个堆栈进行排序 - 编译错误

c - scanf 和 "\n"转义序列

java - 将序列的下一个值与字符串连接起来