https://leetcode-cn.com/conte...python
以前寫過本題目狀態壓縮 dp 解法。
狀態壓縮 dp 首先要檢測 2^m 個狀態是否合法,而後每一行在這些合法的狀態中枚舉出最佳解。時間複雜度高。
看到題解中有網絡流解法原題解是 c++ 版,我寫了 python 版。c++
原方法使用了 Dinic 算法,我這用的更簡單的 Ford-Fulkerson 算法。算法
而後建圖的時候 python 會更加靈活,基本方法是:
額外定義一個源點,一個匯點。個人代碼裏給每一個點編號了,源點固定是 0,匯點固定是 1。第一個 for 循環裏會給圖中,每一個能夠用的座椅從 2 開始編號。(cur 變量初始化爲 2),而後在源點到全部偶數列的點連邊。全部奇數列的點到匯點連邊。(注意這個邊是有向的)數組
edges = [[], []] s = 0 t = 1 cur = 2 for i in range(n): for j in range(m): if seats[i][j] == '.': edges.append([]) if j & 1 == 1: # 偶數 edges[s].append(cur) else: edges[cur].append(t) seats[i][j] = cur cur += 1
編號前:網絡
["#",".","#","#",".","#"] [".","#","#","#","#","."] ["#",".","#","#",".","#"]
編號後:app
['#', 2, '#', '#', 3, '#'] [4, '#', '#', '#', '#', 5] ['#', 6, '#', '#', 7, '#']
編號完成並把源點連偶數列,奇數列連匯點,這個已是一個二分圖了。圖是不聯通的。
而後咱們再把衝突(就是能看到別人答案的)座位兩兩鏈接,可是這個和誰能抄誰的沒有關係,而必須是偶數列鏈接到奇數列。函數
簡化之後的圖是這樣spa
雖然邊是單向的,可是算法中有兩種操做:code
因此還要把反向邊找出來blog
代碼很簡單,就是把 edges 裏的邊顛倒過來。
edges2 = [[] for i in range(len(edges))] for s, e in enumerate(edges): if s == 0: continue for i in e: if i == 1: continue edges2[i].append(s)
一個 dfs 函數。每次尋找一條增廣路徑,直到找不到增廣路徑。
增廣路徑 就是一條從 s(編號 0) 到 t(編號 1) 的簡單路徑(簡單路徑就是無環)。
因此定義一個 vis 數組,標記是否到過某點,防止出現環。
vis = [False] * len(edges)
由於這裏每條邊的流量都是 1。因此咱們只定義一個二維矩陣,能夠表示任意兩個點之間如今是否有流量。
flag = [[False] * len(edges) for _ in edges]
flagi 爲 True 表明 i 到 j 有流量(固然得現有邊才行),有流量就不能正向走,但能夠反向走。沒有流量則相反。
最後答案是 可用的座位數 - 最大流
class Solution: def maxStudents(self, seats: List[List[str]]) -> int: n = len(seats) m = len(seats[0]) edges = [[], []] s = 0 t = 1 cur = 2 for i in range(n): for j in range(m): if seats[i][j] == '.': edges.append([]) if j & 1 == 1: # 偶數 edges[s].append(cur) else: edges[cur].append(t) seats[i][j] = cur cur += 1 if cur == 2: return 0 for i in range(n): for j in range(m): if seats[i][j] != '#': cur = seats[i][j] if j + 1 < m and seats[i][j+1] != '#': if j & 1 == 1: edges[cur].append(seats[i][j+1]) else: edges[seats[i][j+1]].append(cur) if i > 0: if j + 1 < m and seats[i-1][j+1] != '#': if j & 1 == 1: edges[cur].append(seats[i-1][j+1]) else: edges[seats[i-1][j+1]].append(cur) if j - 1 >= 0 and seats[i-1][j-1] != '#': if j & 1 == 1: edges[cur].append(seats[i-1][j-1]) else: edges[seats[i-1][j-1]].append(cur) #for e in enumerate(edges): print(e) #for s in seats: print(s) edges2 = [[] for i in range(len(edges))] for s, e in enumerate(edges): if s == 0: continue for i in e: if i == 1: continue edges2[i].append(s) flag = [[False] * len(edges) for _ in edges] vis = [False] * len(edges) p = [] ans = 0 def dfs(i): #print(i) if i == 1: print('\t', p) return True for j, e in enumerate(edges[i]): if vis[e]: continue vis[e] = True if flag[i][e] == False: flag[i][e] = True p.append(e) r = dfs(e) p.pop() if r: vis[e] = False return r flag[i][e] = False vis[e] = False for j, e in enumerate(edges2[i]): if vis[e]: continue vis[e] = True # print(j, i) if flag[e][i] == True: flag[e][i] = False p.append(e) r = dfs(e) p.pop() if r: vis[e] = False return r flag[e][i] = True vis[e] = False return False while dfs(0): cur -= 1 return cur - 1
歡迎來個人博客: https://codeplot.top/
個人博客刷題分類:https://codeplot.top/categories/%E5%88%B7%E9%A2%98/