Leetcode 5335. 參加考試的最大學生數 maximum students taking exam - 網絡流解法

https://leetcode-cn.com/conte...python

以前寫過本題目狀態壓縮 dp 解法
狀態壓縮 dp 首先要檢測 2^m 個狀態是否合法,而後每一行在這些合法的狀態中枚舉出最佳解。時間複雜度高。
看到題解中有網絡流解法原題解是 c++ 版,我寫了 python 版。c++

解題思路

原方法使用了 Dinic 算法,我這用的更簡單的 Ford-Fulkerson 算法。算法

而後建圖的時候 python 會更加靈活,基本方法是:
額外定義一個源點,一個匯點。個人代碼裏給每一個點編號了,源點固定是 0,匯點固定是 1。第一個 for 循環裏會給圖中,每一個能夠用的座椅從 2 開始編號。(cur 變量初始化爲 2),而後在源點到全部偶數列的點連邊。全部奇數列的點到匯點連邊。(注意這個邊是有向的)數組

image.png

給每一個點編號和與匯點、源點創建邊的代碼

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, '#']

編號完成並把源點連偶數列,奇數列連匯點,這個已是一個二分圖了。圖是不聯通的。
而後咱們再把衝突(就是能看到別人答案的)座位兩兩鏈接,可是這個和誰能抄誰的沒有關係,而必須是偶數列鏈接到奇數列。函數

簡化之後的圖是這樣
image.pngspa

找反向邊

雖然邊是單向的,可是算法中有兩種操做:code

  1. 增大邊上的流(從一個點走正向邊到另外一個點)
  2. 減小邊上的流(從一個點沿着反向邊到另外一個點)

因此還要把反向邊找出來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/

相關文章
相關標籤/搜索