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

Arrays 最小化操作数以使数组的所有元素相等,arrays,algorithm,Arrays,Algorithm,给定一个由n个元素组成的数组,只允许执行2种操作,以使数组中的所有元素相等 将任何元素乘以2 将元素除以2(整数除法) 您的任务是最小化执行的上述操作的总数,以使数组的所有元素相等 范例 数组=[3,6,7]最小运算是2,因为6和7可以除以2得到3 我甚至想不出暴力解决方案 约束条件 1. 通过二进制扩展将所有数字视为0和1的字符串 例如:3、6、7分别表示为11、110、111 除以2相当于删除最右边的0或1,乘以2相当于从右边添加一个0 对于由0和1组成的字符串,让我们将其“head”定义为

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

  • 将任何元素乘以2
  • 将元素除以2(整数除法)
  • 您的任务是最小化执行的上述操作的总数,以使数组的所有元素相等

    范例

    数组=[3,6,7]最小运算是2,因为6和7可以除以2得到3

    我甚至想不出暴力解决方案

    约束条件 1.
  • 通过二进制扩展将所有数字视为
    0
    1
    的字符串
  • 例如:
    3、6、7分别表示为
    11、110、111

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

  • 任务变成查找所有给定字符串的最长公共头,然后确定在此公共头之后要添加多少个
    0

  • 例如:

    假设您有以下字符串:

    10101001
    101011
    10111
    1010001

  • 查找
    10101001
    101011
    的最长公共头,即
    10101
  • 查找
    10101
    10111
    的最长公共头,即
    101
  • 查找
    101
    1010001
    的最长公共头,即
    101
  • 然后您确定所有的数字都应该变成
    101 00…
    格式的数字

    要确定在
    101
    之后要添加多少
    0
    ,请查找每个字符串中紧跟在
    101
    之后的连续
    0

    对于
    10101001
    :1

    对于
    101011
    :1

    对于
    10111
    :0

    对于
    1010001
    :3

    仍然需要找到一个使
    |k-1 |+| k-1 |+| k-0 |+| k-3 |
    最小化的整数
    k
    。这里我们找到
    k=1
    。所以每个数字最后都应该是1010 正如另一个答案所解释的,回溯是不必要的。为了好玩,我们对该方法进行了一点实现。(请参阅底部的联机运行链接):

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

        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)
       })
    
    然后我们需要后缀的长度,而不计算后缀的leeding零:

        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)
            (0 to maxZeros).map{zeros => (is.map{getOperations(_, prefix, zeros)}.sum, prefix << zeros)}.minBy(_._1)
        }
    
    对于你的边界,输入大小基本上是线性的

    此版本可在此处找到:


    提及约束条件时,请不要忘记发布约束条件,因为它们非常重要!蛮力可以很快是的,这是正确的解决方案。一旦你观察到解的形状(前缀后面跟着零),这真的很容易。@WhatsUp如何有效地找到k?鉴于你的限制,甚至可以用bruteforce来做:试试从
    0
    20
    的每一个
    k
    ,比方说。我已经用这个实现来代替我的答案。我已经用蛮力做到了,如果速度不够快,你可以在前缀后面按0的数字分组,因为这是唯一重要的事情,并将其转化为一个因素。这个答案符合问题的要求吗?只允许两种类型的操作;乘二除二。二进制扩展将需要更多的运算,额外的计算,如加法或减法,也将超出初始问题要求的范围。结果是正确的,但旅程似乎越界了。也许我误解了这个问题?
        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)
        }
    
        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)