【編程的樂趣-用python解算法謎題系列】謎題一 保持一致

謎題一 保持一致

謎題

假設有一大羣人排隊等待觀看棒球比賽。他們都是主場球迷,每一個人都戴着隊帽,但不是全部人都用同一種戴法,有些人正着戴,有些人反着戴。python

假定你是保安,只有在全組球迷帽子戴法一致時才能讓他們進入球場,要麼所有正着戴,要麼所有反着戴。由於每一個人對正戴和反戴的定義並不相同。所以你不能對他們說把帽子正着戴或反着戴,只能告訴他們轉一下帽子。算法

舉個栗子(咱們用 F 表示正戴,B 表示反戴)app

F F B B B F B B B F F B F工具

上面是一個13人的隊伍,位置從0 ~ 12。你能夠發出如下指令:優化

請 0 號位置的人轉一下帽子,編碼

請 1 號位置的人轉一下帽子,code

......orm

而後分別對5,9,10,12號位置的人發出一樣的指令,總共要發出六次指令。字符串

咱們也能夠發出這樣的指令:string

請 2~4 號位置的人轉一下帽子,

請 6~8號位置的人轉一下帽子,

請 11 號位置的人轉一下帽子。

只須要 3 次指令。

咱們的要求是讓保安生成的命令數最少。難度更大的問題是:可否第一次沿着隊伍就得正確答案呢?

算法

算法一 尋找想法相同的連續人員

計算正戴區間和反戴區間的個數, 區間數更少的便是咱們要反轉的帽子區間。

def pleaseconform(caps):
    section = [] # 統計各區間的列表
    start = 0
    Fnum = 0 # 正戴區間數
    Bnum = 0 # 反戴區間數
    for i in range(1,len(caps)):
        if caps[start] != caps[i]: # 標誌着新區間的產生
            section.append([start , i-1 ,caps[start]])
            if caps[start]=='F':
                 Fnum += 1
            else:
                Bnum += 1
            start = i
    section.append([start,len(caps)-1,caps[start]]) # 6~13行代碼未添加最後一個區間,這行代碼用於添加最後一個區間
    if caps[start]=='F':
        Fnum += 1
    else: 
        Bnum +=1
    if Fnum>Bnum :
        flag = 'B'
    else:
        flag = 'F'
    for t in section:
        if t[2]==flag:
            if(t[0] == t[1]):
                print("請"+str(t[0])+"號位置的人反轉帽子")
            else:
                print("請"+str(t[0])+"到"+str(t[1])+"的人反轉帽子")
   
caps = ['F','F','B','B','B','F','B','B','B','F','F','B','F']    
pleaseconform(caps)

"""
Output:
        請2到4的人反轉帽子                               
        請6到8的人反轉帽子                                   
        請11號位置的人反轉帽子
"""
  • 咱們注意到算法一的核心代碼 6~13 行未添加最後一個區間,所以咱們要再添加代碼來完善算法,顯得過於繁瑣。實際上,咱們只須要在 caps 列表添加一個其餘元素如'M',就能夠消除掉這種狀況。
# 代碼優化
def pleaseconform(caps):
    caps.append('M')
    section = []
    start = 0
    Fnum = 0
    Bnum = 0
    for i in range(1,len(caps)):
        if caps[start] != caps[i]:
            section.append([start , i-1 ,caps[start]])
            if caps[start]=='F':
                 Fnum += 1
            else:
                Bnum += 1
            start = i
    if Fnum>Bnum :
        flag = 'B'
    else:
        flag = 'F'
    for t in section:
        if t[2]==flag:
            if(t[0] == t[1]):
                print("請"+str(t[0])+"號位置的人反轉帽子")
            else:
                print("請"+str(t[0])+"到"+str(t[1])+"的人反轉帽子")
 
caps = ['F','F','B','B','B','F','B','B','B','F','F','B','F']    
pleaseconform(caps)

"""
Output:
        請2到4的人反轉帽子                               
        請6到8的人反轉帽子                                   
        請11號位置的人反轉帽子
"""
算法二 單遍算法one pass

經過觀察,實際上咱們只須要經過 caps 列表中第一隻帽子的方向,就能夠得出咱們須要反轉的是正戴區間仍是反戴區間。由於第一隻帽子方向區間的個數必定大於等於另外一方向的區間數。基於這一觀察,可以實現一個one pass 算法。

# one pass 
def pleaseconformonepass(caps):
    caps.append(caps[0])
    for i in range(1,len(caps)):
        if(caps[i] != caps[i-1]):
            if(caps[i] != caps[0]):
                print("請"+str(i)+"號位置到")
            else:
                print(str(i-1)+"號位置的人反轉帽子")
            
pleaseconformonepass(caps)

"""
Output:
        請2號位置到                                           
        4號位置的人反轉帽子                                     
        請6號位置到                                         
        8號位置的人反轉帽子                                    
        請11號位置到                                      
        11號位置的人反轉帽子    
"""

謎題背後

這道謎題背後的出發點是數據壓縮。向同一方向的人發出的命令信息是相同的,能夠被壓縮爲一組較少的命令,其中每一條命令指揮一組連續的人。

謎題拓展

數據壓縮有多種實現方式,在思路上接近於這道習題的一種算法叫作遊程編碼。舉一個最簡單的例子最容易描述,假設有如下字符串:

WWWWWWWWWWWWWBBWWWWWWWWWWWWBBBBB

使用遊程編碼算法,咱們能夠把上述字符串壓縮爲一個由數字和字符構成的字符串:

13W2B12W5B

遊程解碼算法就是把' 13W2B12W5B '解壓爲原始字符串的過程。

現代計算機的壓縮工具,即是利用了這種思想相關的算法。

如下是遊程編碼解碼的具體實現:

def youcengbianma(string):
    start=0
    newstring = ''
    for i in range(1,len(string)):
        if(string[start]!=string[i]):
            newstring += str(i-start)
            newstring += string[start]
            start=i
    newstring += str(i-start+1)
    newstring += string[start]
    return newstring

print(youcengbianma("wwweeewwwweeffeee"))

"""
Output:
        3w3e4w2e2f3e
"""

def youcengjiema(string):
    num = ''
    newstring = ''
    for i in range(0,len(string)):
        if(not string[i].isalpha()): # isalpha() 若是是字母字符,返回true
            num += string[i]
        else:
            for j in range(0,int(num)):
                newstring += string[i]
            num = ''
    return newstring

print(youcengjiema("3w3e4w2e2f3e")

"""
Output:
        wwweeewwwweeffeee
"""
相關文章
相關標籤/搜索