zoukankan      html  css  js  c++  java
  • SRM596 DIV1 250

    开始以为是DP然后用了一个时间复杂度数量级为(10^7)的DP来做,结果case 35超时,后来发现就是一个逻辑分析题,就知道有贪心策略的,250分的题怎么可能是DP呢。

    分析

    首先考虑一个数的情况,任意一个数a的形成可以通过如下构造来表示:

    (egin{equation}egin{split}a&=((((0+n_0) imes2+n_1) imes2+n_2) imes2+...+n_{k-1}) imes2+n_{k}\&=sum_{i=0}^{k} n_i imes2^{k-i} (n^i in N) end{split}end{equation})

    将连续的(+1)操作看成(+n)不难得出以上变换,因此a的变换可以看成一个特殊的二进制数,跟一般的二进制数不同之处在于每位的数字可以超过1。

    将a重新表示成:

    (a=overline{n_0n_1n_2...n_k}quad(例: 10=overline{210}=overline{0106}=overline{1010})) 

    注意该种表示法的以下特点:

    1. a的前缀零意味着开始一直在做Doubling操作

    2. 各种结构可以通过进位(当前位-2,高一位+1)和借位(当前位+2,高一位-1)操作相互变换

    3. k就是Doubling操作的次数

    从0变换到a所需的操作数s等于:

    (s(overline{n_0n_1n_2...n_k})=k+sum_{i=0}^{k}n_iquad(overline{n_0n_1n_2...n_k}=a))

    当a表示为一般二进制数且没有多余前缀零的时候可令s最小。注意到任何一次进位操作可以使s变小或保持不变,所以尽可能的进位最后得出的数形就是一般二进制数。

    现在考虑多个数(a_i)的情况,每个数可以转变为各自的特殊二进制数,但是由于Doubling操作的存在使得它们的位数必须相等(前缀零也要占位,如果有的话)。

    令Doubling操作的次数为t,若已知t,除去总的一起做的Doubling操作,最优解一定是限制条件下每个数单独的最优解之和,单独最优解(s(a,t))可以是将a尽可能进位的结果,即:

    ( egin{equation}egin{split} s(a,t)_{min} &= s(overline{n_0n_1...n_t})_{min}quad&(a=overline{n_0n_1...n_t}) \ &= s(overline{n_0n_1...n_t})-tquad&(igeq1, n_i < 2)(注意t要被省去) \ &= sum_{i=0}^{t}n_i \ &= a // 2^t + sum_{i=1}^{t}n_i quad&(n_0=a // 2^t)end{split}end{equation} )

    最后,枚举t计算所有可能情况下的最小值,总步骤数tot:

    (tot(t)={t+sum_{i}s(a_i, t)_{min}|tin[0,10]})

    (tot_{min}=min(tot(t)))

    优化

    进一步可以证明当 (t+1) 为所有数的二进制位数的最大值的时候,(tot) 取最小值 (tot_{min}),证明如下:

    令 (len(a)) 表示数a的一般二进制数的位数减一,若 (t < max(len(a_i))) 则:

    (egin{equation}egin{split}tot(t+1)-tot(t)&=1+sum_{i}^{len(a_i)>t}(s(a_i, t+1)-s(a_i, t)) \ &=1+sum_{i}^{len(a_i)>t}(a_i//2^{t+1}+a_1-a_i//2^t) quad &(a_1是t+1位的情况) \ &=(1+sum_{i}^{len(a_i)>t}a_1)+(sum_{i}^{len(a_i)>t}(-a_i//2^{t+1})) \ &leq 1-a_k//2^{t+1} leq 0 quad &(len(a_k)=max(len(a_i)))  end{split}end{equation})

    所以有 ( tot(t+1) leq tot(t))

    则:

    令 (sum(a)) 为a的一般二进制数的各位数字之和:

    (tot_{min}=max(len(a_i)) + sum_{i}sum(a_i))

    注意这道题如果没有考虑到前缀零也可以占位的情况就会直接得出「t可以取所有数的一般二进制数的位数的最小值」的错误结论,好在题目里有测试点可以测出这个bug,所以不会被无情challenge

    class IncrementAndDoubling:            
        def getMin(self, a):
            x = max(a)
            maxk = 0
            while True:
                maxk += 1
                x = x // 2
                if x == 0:
                    break
    
            s = maxk - 1
            for x in a:
                for i in range(maxk):
                    s += x >> i & 1
            return s
    
    
    # test
    o = IncrementAndDoubling()
    
    # test 0
    assert(o.getMin((0,)) == 0)
    
    # test case
    assert(o.getMin((2,1)) == 3)
    assert(o.getMin((16,16,16)) == 7)
    assert(o.getMin((100,)) == 9)
    assert(o.getMin((0, 0, 1, 0, 1)) == 2)
    assert(o.getMin((123, 234, 345, 456, 567, 789)) == 40)
    assert(o.getMin((7,5,8,1,8,6,6,5,3,5,5,2,8,9,9,4,6,9,4,4,1,9,9,2,8,4,7,4,8,8,6,3,9,4,3,4,5,1,9,8,3,8,3,7,9,3,8,4,4,7)) == 84)
    print('ok')
    View Code
  • 相关阅读:
    面试题 Comparable、Comparator 比较
    Longest Palindromic Substring
    Permutation Sequence
    Spring Boot_打造企业级微信点餐系统_汇总贴
    小D课堂
    小D课堂
    小D课堂
    小D课堂
    小D课堂
    小D课堂
  • 原文地址:https://www.cnblogs.com/valaxy/p/3430838.html
Copyright © 2011-2022 走看看