1. 程式人生 > 實用技巧 >ZOJ 3725 概率dp

ZOJ 3725 概率dp

  1. 以下假設變數 a 為 10, b為 20: and x and y 布林"與" - 如果 x 為 False,x and y 返回 False,否則它返回 y 的計算值。 (a and b) 返回 20。

    百度之後得到結論:

    python 中的 and 從左到右計算表示式,若所有值均為真,則返回最後一個值,若存在假,返回第一個假值;

    or 也是從左到有計算表示式,返回第一個為真的值;

    其中數字 0 是假,其他都是真;

    字元 "" 是假,其他都是真;

  2. 一開始輸入了

    a = 00111100

    這麼個賦值語句被提示了錯誤,於是去搜了下相關的部落格得知 python 中數字有以下的表示方式:

    2 進位制是以 0b 開頭的: 例如: 0b11 則表示十進位制的 3

    8 進位制是以 0o 開頭的: 例如: 0o11 則表示十進位制的 9

    16 進位制是以 0x 開頭的: 例如: 0x11 則表示十進位制的 17

    但是在測試的時候又遇到了個問題,那就是輸出來的被自動轉化成了十進位制:

    >>> a=0b111100
    >>> a
    60

    於是又去找了怎麼輸出二進位制,得到了以下內容:

    分別使用 bin,oct,hex 可輸出數字的二進位制,八進位制,十六進位制形式,例如:

    >>> a=0b111100
    >>> a=60
    >>> bin(a)
    '0b111100'
    >>> oct(a)
    '0o74'
    >>> hex(a)
    '0x3c'

    疑問解決!

  3. 剛開始學 python,當想要自增運算的時候很自然的 a++,結果發現編譯器是不認識 ++ 的,於是去網上搜了一下,結果發現一篇老外的問答很精彩,涉及到了 python 這個語言的設計原理。

    問題無外乎就是 python 沒有自增運算子,自增操作是如何實現的。

    回答中有人介紹了關於自增操作,python 不使用 ++ 的哲學邏輯:編譯解析上的簡潔與語言本身的簡潔,就不具體翻譯了。

    後面還有老外回答並附帶了一個例子非常的精彩,指出了 python 與 c 語言概念上的一些差異,語言描述的可能未必準確,直接上例子:

    >>> b = 5  
    >>> a = 5  
    >>> id(a)  
    162334512  
    >>> id(b)  
    162334512  
    >>> a is b  
    True

    可以看出, python 中,變數是以內容為基準而不是像 c 中以變數名為基準,所以只要你的數字內容是5,不管你起什麼名字,這個變數的 ID 是相同的,同時也就說明了 python 中一個變數可以以多個名稱訪問。

    這樣的設計邏輯決定了 python 中數字型別的值是不可變的,因為如果如上例,a 和 b 都是 5,當你改變了 a 時,b 也會跟著變,這當然不是我們希望的。

    因此,正確的自增操作應該 a = a + 1 或者 a += 1,當此 a 自增後,通過 id() 觀察可知,id 值變化了,即 a 已經是新值的名稱。

  4. 糾正一下樓上的一些觀點

    樓上的同學所說的在指令碼式程式設計環境中沒有問題。但是在互動式環境中,編譯器會有一個小整數池的概念,會把(-5,256)間的數預先建立好,而當a和b超過這個範圍的時候,兩個變數就會指向不同的物件了,因此地址也會不一樣,比如下例:

    >>> a=1000
    >>> b=1000
    >>> id(a);id(b)
    2236612366224
    2236617350384
    >>>
  5. 位運算,是自己平時最不熟悉的一塊,相信很多人也是這樣,但巧妙的運用位運算可以來解決很多題目,例如,劍指offer上面的一道:

    輸入一個整數,輸出該數二進位制表示中1的個數。其中負數用補碼錶示。

    # -*- coding:utf-8 -*-
    
    class Solution:
        def NumberOf1(self, n):
            # write code here
            cnt = 0
            if n<0:
                n = n & 0xffffffff
            while n:
                cnt+=1
                n = (n-1) & n
            return cnt

    通過按位與,巧妙的計算出二進位制中"1"的個數。

  6. is 和 ==

    is 判斷兩個變數是否是引用同一個記憶體地址。

    == 判斷兩個變數是否相等。

    如果不用 a = b 賦值,int 型時,在數值為 -5~256(64位系統)時,兩個變數引用的是同一個記憶體地址,其他的數值就不是同一個記憶體地址了。

    也就是,a b 在 -5~256(64位系統)時:

    a = 100
    b = 100
    a is b # 返回 True

    其他型別如列表、元祖、字典讓 a、b 分別賦值一樣的時:

    a is b  # 返回False
  7. == 和 is 的區別

    is 判斷兩個物件是否為同一物件, 是通過 id 來判斷的; 當兩個基本型別資料(或元組)內容相同時, id 會相同, 但並不代表 a 會隨 b 的改變而改變。

    == 判斷兩個物件的內容是否相同, 是通過呼叫 __eq__() 來判斷的。

    1、當列表,元組,字典中的值都引用 a,b 時,總是返回 True,不受 a,b 值大小的影響

    a=1000
    b=1000
    list1=[a,3,5]
    list2=[b,4,5]
    print(list1[0] is list2[0])
    tuple1=(a,3,5)
    tuple2=(b,4,5)
    print(tuple1[0] is tuple2[0])
    dict1={6:a,2:3,3:5}
    dict2={1:b,2:4,3:7}
    print(dict1[6] is dict2[1])

    輸出結果為:

    True
    True
    True

    2、當不引用a,b,直接用具體值來測試時,列表,字典,不受值大小影響,返回True,元組則受 256 值範圍的影響,超出範圍則地址改變,返回 False。

    list1=[1000,3,5]
    list2=[1000,4,5]
    print(list1[0] is list2[0])
    tuple1=(1000,3,5)
    tuple2=(1000,4,5)
    print(tuple1[0] is tuple2[0])
    dict1={6:1000,2:3,3:5}
    dict2={1:1000,2:4,3:7}
    print(dict1[6] is dict2[1])

    輸出結果為:

    True
    False
    True

    3、當直接用列表、元組、字典本身來測試時,剛好相反,元組返回 True,列表,字典返回 False。

    list1=[1000,3,5]
    list2=[1000,3,5]
    print(list1 is list2)
    tuple1=(1000,3,5)
    tuple2=(1000,3,5)
    print(tuple1 is tuple2)
    dict1={1:1000,2:3,3:5}
    dict2={1:1000,2:3,3:5}
    print(dict1 is dict2)

    輸出結果為:

    False
    True
    False
  8. 關於 is 和 == 的標識問題

    (1)只要是變數的值相同,標識都相同,沒有-5~256的限制,看下面的例子:

    a = 100000
    b = 100000
    if a is b:
        print('a 和 b 標識相同,標識為:',id(a))
    else:
        print('a 和 b 標識不相同,a 標識為:',id(a),'b 標識為:',id(b))

    輸出結果為:

    a  b 標識相同,標識為: 2158477874512

    (2)同樣的如果是負數,仍然沒有上述限制:

    a = -100000
    b = -100000
    if a is b:
        print('a 和 b 標識相同,標識為:',id(a))
    else:
        print('a 和 b 標識不相同,a 標識為:',id(a),'b 標識為:',id(b))

    輸出結果為:

    a  b 標識相同,標識為: 2137845175632

    (3)列表也是一樣的,只要是列表項數值一樣,那麼標識也是一樣的。例子如下:

    list1 = [10000,20000,30000]
    list2 = [10000,12000,15000]
    if list1[0] is list2[0]:
        print('list1[0] 和 list2[0] 標識相同,標識為:',id(list1[0]))
    else:
        print('list1[0] 和 list2[0] 標識不相同,list1[0]標識為:',id(list1[0]),'list2[0]標識為:',id(list2[0]))

    輸出結果為:

    list1[0]  list2[0] 標識相同,標識為: 1375086286224

    (4)元組的標識是跟著變數名的,變數名不一樣,標識也不一樣,上例子:

    tuple1 = (10000,20000,30000)
    tuple2 = (10000,12000,15000)
    if tuple1[0] is tuple2[0]:
        print('tuple1[0] 和 tuple2[0] 標識相同,標識為:',id(tuple1[0]))
    else:
        print('tuple1[0] 和 tuple2[0] 標識不相同,tuple1[0] 標識為:',id(tuple1[0]),'tuple2[0]標識為:',id(tuple2[0]))

    輸出結果為:

    tuple1[0]  tuple2[0] 標識不相同,tuple1[0] 標識為: 1375086286384 tuple2[0]標識為: 1375086286480

    (5)字典和列表是一樣的,只要是列表項數值一樣,那麼標識也是一樣的。例子如下:

    dict1 = {1:10000,2:20000,3:30000}
    dict2 = {1:10000,2:12000,3:15000}
    if dict1[1] is tuple2[1]:
        print('dict1[1] 和 tuple2[1] 標識相同,標識為:',id(dict1[1]))
    else:
        print('dict1[1] 和 tuple2[1] 標識不相同,dict1[1] 標識為:',id(dict1[1]),'tuple2[1] 標識為:',id(dict2[1]))

    輸出結果為:

    dict1[1]  tuple2[1] 標識不相同,dict1[1] 標識為: 1375086286224 tuple2[1] 標識為: 1375086286224
  9. 深刻理解 and、or 邏輯運算子:

    print(0 and 1)      # =>0,0等同於False
    print(False and 1)  # =>False
    print(-1 and 1)     # =>1
    print(1 or False)   # =>1,非零等同於True
    print(True or False)# =>True
    print(-1 or 0)      # =>-1
    • and:前面為假(0 或者 False)則表示式為假,否則表示式為後面的值;
    • or:前面為真(非 0 或者非 False)則表示式為前面的值,否則表示式為後面的值;

    優先順序:not>and>or

    print(1 and 0 or  not False) #=>True
    print( not False or 1 and 0) #=>True
    print( 1 or not True and 0)  #=>1
  10. 看了 "is" 和 "==" 的區別,我有了進一步的瞭解。

    is 是比較物件是否相同(is 表示物件識別符號即 object identity),即用 id() 函式檢視的地址是否相同,如果相同則返回 True,如果不同則返回 False。is 不能被過載。

    == 是比較兩個物件的值是否相等,此操作符內部呼叫的是 __eq__() 方法。所以 a==b 等效於a.__eq__(b),所以 = 可以被過載。

  11. and or not

    優先順序:

    () > not > and > or

    1.or

    在 Python 中,邏輯運算子 or,x or y, 如果 x 為 True 則返回 x,如果 x 為 False 返回 y 值。因為如果 x 為 True 那麼 or 運算就不需要在運算了,因為一個為真則為真,所以返回 x 的值。如果 x 的值為假,那麼 or 運算的結果取決於 y,所以返回 y 的值。

    print(1 or 2)   # 1
    print(3 or 2)   # 3
    print(0 or 2)   # 2
    print(0 or 100) # 100
    print(0 or 0)

    2.and

    在 Python 中,邏輯運算子 and,x and y,如果 x 為 True 則返回 y 值。如果 x 為 False 則返回 x 值。如果 x 的值為 True,and 的運算不會結束,會繼續看 y 的值,所以此時真與假取決於 y 的值,所以 x 如果為真,則返回 y 的值。如果 x 為假,那麼 and 運算就會結束運算過程了,因為有一個為假則 and 為假,所以返回 x 的值。

    print(1 and 2)  # 2
    print(3 and 0)  # 0
    print(0 and 2)  # 0
    print(3 and 2)  # 2
    print(0 and 0)  # 0

    3.混合例子與解析

    按照從左向由,優先順序高的先執行優先順序高的規則,首先因為比較運算子優先順序高於邏輯運算子,很簡單,如果運算子低於了邏輯運算子優先順序那還如何運算呢。and 優先順序大於 or,not 優先順序大於 and 和 or。

    not 4 > 5 為 True

    1 > 2 為 False

    3 < 2 為 False

    Flase and 3 為 False,因為False為假所以and不在運算直接返回False

    4 and False 為 False,因為 4 為真所以 and 運算子會繼續運算後面的,以 False 為主,所以返回 False。

    False or False 為 False

    False or True 為 True

    False or False 為 False,因為 False 為假,所以 or 運算子會繼續運算後面的,以 False 為主,所以返回後面的 False 值

  12. 劍指offer上面的一道:

    # -*- coding:utf-8 -*-
    class Solution:
        # bin函式: bin返回一個整數的二進位制字串,以0b開頭,
        # bin(10) '0b1010'  bin(-10)  '-0b1010'
        #
        # count函式 返回字串當中非重疊的字串的個數,可以傳入start,end來表示對字串切片的結果
        #
        #如果一個數為負數,那麼2**32 + n 然後再用bin返回的就是它的補碼形式。 補碼+原始碼=2**32
        def NumberOf1(self, n):
            if n >= 0:
                return bin(n).count('1')
            else:
                return bin(2**32 + n).count('1')
     
    if __name__ == '__main__':
        solution = Solution()
        print(solution.NumberOf1(10))
        print(solution.NumberOf1(-10))
  13. 除了使用python內建的bin函式和字串計數方法count求二進位制資料的1的個數外,用自己的表示式求解可如下做:

    a = 60 #其二進位制值為0b00111100
    n = 0 #用於計數
    while a: #用移位方法求解,直到a移位為0為止
        if a & 1 == 1:
            n += 1 #將a與1進行位與操作,即可知道a的最後1位是否為1,若為1,則計數n增1,不然則無需變化n的值
        a >>= 1 #測試了a的最後一位後,將最後一位右移移除,重新賦值給a
    print(n) #列印最後的計數資料

    以上程式碼顯示n=4,計算正確。