正如另一个答案所解释的,不需要回溯。为了好玩,对这种方法进行了一点实现。 (见底部在线运行链接):
首先我们需要一个函数来确定数字中的二进制位数:
def getLength(i: Int): Int = {
@annotation.tailrec
def rec(i: Int, result: Int): Int =
if(i > 0)
rec(i >> 1, result + 1)
else
result
rec(i, 0)
}
那么我们需要一个函数来判断两个等长数的公共前缀
@annotation.tailrec
def getPrefix(i: Int, j: Int): Int =
if(i == j) i
else getPrefix(i >> 1, j >> 1)
一个任意数字的列表:
def getPrefix(is: List[Int]): Int = is.reduce((x,y) => {
val shift = Math.abs(getLength(x) - getLength(y))
val x2 = Math.max(x,y)
val y2 = Math.min(x,y)
getPrefix((x2 >> shift), y2)
})
那么我们需要后缀的长度而不计算后缀的零:
def getSuffixLength(i: Int, prefix: Int) = {
val suffix = i ^ (prefix << (getLength(i) - getLength(prefix)))
getLength(suffix)
}
现在我们可以计算将操作 i 与附加“零”零的前缀同步所需的操作数。
def getOperations(i: Int, prefix: Int, zeros: Int): Int = {
val length = getLength(i) - getLength(prefix)
val suffixLength = getSuffixLength(i, prefix)
suffixLength + Math.abs(zeros - length + suffixLength)
}
现在我们可以找到最少的操作数并将其与我们将同步到的值一起返回:
def getMinOperations(is: List[Int]) = {
val prefix = getPrefix(is)
val maxZeros = getLength(is.max) - getLength(prefix)
(0 to maxZeros).map{zeros => (is.map{getOperations(_, prefix, zeros)}.sum, prefix << zeros)}.minBy(_._1)
}
您可以在以下位置尝试此解决方案:
http://goo.gl/lLr5jl
找到正确数量的零的最后一步可以改进,因为只有没有前导零的后缀的长度很重要,而不是它的样子。所以我们可以通过计算有多少来计算我们需要的操作数:
def getSuffixLength(i: Int, prefix: Int) = {
val suffix = i ^ (prefix << (getLength(i) - getLength(prefix)))
getLength(suffix)
}
def getMinOperations(is: List[Int]) = {
val prefix = getPrefix(is)
val maxZeros = getLength(is.max) - getLength(prefix)
val baseCosts = is.map(getSuffixLength(_,prefix)).sum
val suffixLengths: List[(Int, Int)] = is.foldLeft(Map[Int, Int]()){
case (m,i) => {
val x = getSuffixLength(i,prefix) - getLength(i) + getLength(prefix)
m.updated(x, 1 + m.getOrElse(x, 0))
}
}.toList
val (minOp, minSol) = (0 to maxZeros).map{zeros => (suffixLengths.map{
case (x, count) => count * Math.abs(zeros + x)
}.sum, prefix << zeros)}.minBy(_._1)
(minOp + baseCosts, minSol)
}
所有腋窝手术只需要最大数量大小的对数时间。我们必须通过孔列表来收集后缀长度。然后我们必须猜测在最大数量的许多零中最多有对数的零的数量。所以我们应该有一个复杂度
O(|list|*ld(maxNum) + (ld(maxNum))^2)
所以对于你的界限,这基本上是输入大小的线性。
这个版本可以在这里找到:
http://goo.gl/ijzYik