1. 程式人生 > >leetcode 672之Bulb Switcher II

leetcode 672之Bulb Switcher II

原題:

我用中文簡述一下題幹:

上面的四個方塊是四個燈泡按鈕,

下面的一系列橢圓是一組燈泡,初始狀態全亮。

上面四個按鈕的作用分別是:

全:按一下則所有燈泡切換狀態(從亮到黑, 或從黑到亮)

偶:所有偶數位燈泡切換狀態

奇:所有奇數位燈泡切換狀態

隔三: 所有1+3k(k=0,1,2...)燈泡切換狀態(也就是圖中黃色標註的這些位置)

現在給定燈泡個數n,以及確定的按燈泡的次數m(每次只能按一個按鈕),求最終燈泡序列的狀態組合有可能有多少種。

例如n=1,m=1,則最終燈泡序列的狀態可能為[on], [off],即2種,返回2

再如n=2,m=1, 則最終燈泡序列的狀態可能為 [on, off], [off, on], [off, off],即3種,返回2.

====以上是我對題乾的解讀,總感覺有時候Leetcode題乾的表述不是那麼清晰。不過我這裡翻譯如此。

解題思路:

關於最終狀態的求解,本質上是從初始狀態經過一些列操作演化而來,那麼如果可以先求出這一系列按燈泡操作本身的組合數x,就相當於確定了最終燈泡狀態的上限,再排除這x個組合中可能導致燈泡狀態結果重合的情況(即兩種不同的操作序列導致同一個燈泡狀態的情況),就可以求出最終結果。

求這個操作組合數x,即四個按鈕按m次,換一種思路就是:有四個槽,往裡投幣m次,可重複投同一個槽,最終槽位狀態會是裡面分別有多少個幣。然而再細想一下,這個問題還可以做一個等價的轉換,每個槽只要有2個硬幣,就可以像俄羅斯方塊一樣消去,因為同一個按鈕按任意偶數次都相當於沒按,那麼依照這種思路,最終每槽位中可能的狀態只能是0或者1,分別代表不按或者按,考慮到槽位總共只有4個,那麼槽位狀態的組合最多隻有2^4=16種,這就是我們要求的最終燈泡狀態的上限。這一點很重要,如果不做這樣的等價轉換,則組合數可能非常大,再從這些組合數中去推敲另一個組合數,計算量就會很大。

那麼如何求出具體有哪些按鈕操作組合情況呢?既然上面說了上限只有16種,所以是一個跟m規模無關的問題,與其根據m去求,不如拿16種狀態一個個去驗證, 驗證通過即為一種組合情況。為了方便,可以用4位二進位制數來代表(從0000到1111),例如1001表示按“全”和“隔三”兩個按鈕, 1100,代表按“全”和“偶”兩個按鈕,以此類推。。。有了這種代表方式後,如何驗證m次操作可以達到這種等價狀態呢?很簡單,考慮這個例子:1110這個狀態能否通過5次操作達到等價操作?可以,因為這個序列中有3個1,那麼我先把5次中的三次按照順序給按了,剩下兩次(偶數次)隨便挑一個按鈕給按了(上面說了,任意按偶數次按鈕等價於沒按),根據這個例子可以知道一個原則,我們先看最後這個狀態序列碼中有多少個1,再拿m去比對,如果m比1的個數還少,那麼顯然達不到,如果正好相等,那麼一定可以達到,如果m大於這個數,則看大出多少,如果大出的數是偶數,則可以達到。

通過以上方式,就可以求出所有m次操作可達的等價操作序列。下一步就是確定兩個操作序列應用於n個燈泡時,是否可能達到同一個燈泡序列狀態,這一步求解思路如下:假如我們要判斷1001和1110這兩個操作序列能否使燈泡達到同狀態,可以通過找這兩個序列碼的“編輯距離”來算,所謂“編輯距離”就是從狀態A變化到狀態B要經過哪些碼位變換(即同一位置的狀態翻轉),例如從1001到1110需要經過哪些變換呢?首先,第一位相同,不需要變換,變換操作記為0,第二位狀態不同,變換操作記為1,第三位第四位也不同,都記為1,那麼可以記為兩種狀態的“編輯距離為0111,也就是從1001這個狀態切換到1110這個狀態,只需要做0111對應的操作即可達到。

那麼再回過頭來看,我們的目的是求1001和1110這兩種操作序列能否達到相同同燈泡狀態,而我們又說了通過0111這個操作可以從1001達到1110,那麼可想而知,可達相同燈泡狀態的等價於說這個我們操作了這個0111這個操作等價於不改變任何燈泡狀態。或者換種說法,有三種操作序列A, B, C,分別對應一組按鈕操作,並且A+B=C, 如果我們知道A和C對應著同樣的燈泡序列,那麼B必然意味著等價於啥也沒做。而“啥也沒做”意味著對任意燈泡,都啥也沒做或者做了偶數次同按鈕操作。

以上就是全部的思路。程式碼實現如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

class Solution:

def ones(self,num):

# Note that bin is a built-in

return bin(num).count('1')

def reacheable(self, i, m):

one_count = self.ones(i)

if(m<one_count):

return False

elif (m==one_count):

return True

else:

return ((m-one_count)%2==0)

def button_composer(self, m):

raw_list_of_comb = []

for in range(016):

if(self.reacheable(i, m)):

raw_list_of_comb.append(i)

return raw_list_of_comb

def button_compose_reducer(self, n, list_of_comb):

def pressAllButton(num):

return (num&8)>0

def pressEvenButton(num):

return (num&4)>0

def pressOddButton(num):

return (num&2)>0

def pressTripButton(num):

return (num&1>0)

def no_effect(nm, op):

for index in range(nm):

opcnt = 0

label =  index+1

if(label%2==0 and pressEvenButton(op)):

opcnt+=1

if(label%2!=0 and pressOddButton(op)):

opcnt+=1

if((label-1)%3==0 and pressTripButton(op)):

opcnt+=1

if(pressAllButton(op)):

opcnt+=1

if(opcnt%2!=0):

return False

return True

def same_effect(nm, elm, elm2):

distance_op = elm ^ elm2

return no_effect(nm, distance_op)

base_set = [list_of_comb[0]]

for in range(1len(list_of_comb)):

breakout = False

for el in base_set:

if (same_effect(n, el, list_of_comb[i])):

breakout=True

break;

if(not breakout):

base_set.append(list_of_comb[i])

return len(base_set)

def flipLights(self, n, m):

"""

:type n: int

:type m: int

:rtype: int

"""

raw_list_of_comb = self.button_composer(m)

result = self.button_compose_reducer(n, raw_list_of_comb)

return result

sol = Solution()

print(sol.flipLights(2,1))

已通過leetcode online judge