最小化操作次数以使数组的所有元素相等

时间:2016-02-13 13:03:52

标签: arrays algorithm

给定n个元素的数组,只允许执行2种操作以使数组的所有元素相等。

  1. 将任意元素乘以2
  2. 将元素除以2(整数除法)
  3. 您的任务是尽量减少为使阵列的所有元素相等所执行的上述操作的总数。

    实施例

    array = [3,6,7]最小操作为2为6,7可以除以2得到3。

    我甚至想不到蛮力解决方案。

    约束 1< = n< = 100000和 1< = ai< = 100000 其中ai是数组的第i个元素。

2 个答案:

答案 0 :(得分:4)

  1. 通过二进制扩展将所有数字视为01的字符串。
  2. 例如:3, 6, 7分别表示为11, 110, 111

    1. 除以2相当于删除最右侧的01,乘以2相当于从右侧添加0
    2. 对于由01组成的字符串,让我们将其“head”定义为子字符串,该字符串是字符串的左边几个术语,以1结尾。 例如:1100101有头111110011100101

      1. 任务变为找到所有给定字符串中最长的共同头部,然后确定在这个共同头部之后添加多少0
      2. 一个例子:

        假设您有以下字符串:

        10101001101011101111010001

        1. 找到10101001101011的最长公共头,即10101;
        2. 找到1010110111的最长公共头,即101;
        3. 找到1011010001的最长公共头,即101
        4. 然后您确定所有数字都应该成为101 00...形式的数字。

          要确定0之后要添加的101个,请在每个字符串中找到0后面的连续101个数:

          10101001:1

          101011:1

          10111:0

          1010001:3

          仍然需要找到一个最小化k的整数|k - 1| + |k - 1| + |k - 0| + |k - 3|。我们在这里找到k = 1。所以每个号码最后应该是1010

答案 1 :(得分:2)

正如另一个答案所解释的那样,回溯是没有必要的。为了它的乐趣,该方法的一点实现。 (参见底部在线运行的链接):

首先,我们需要一个确定数字中二进制数字位数的函数:

    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与前缀同步的操作数量&#34; zeros&#34;附加零。

    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