天天看點

藍橋杯剪郵票(遞歸組合 + 連通性判斷)

1. 問題描述:

如下圖, 有12張連在一起的12生肖的郵票。現在你要從中剪下5張來,要求必須是連着的。(僅僅連接配接一個角不算相連) 

藍橋杯剪郵票(遞歸組合 + 連通性判斷)

比如,下面兩張圖中,粉紅色所示部分就是合格的剪取

藍橋杯剪郵票(遞歸組合 + 連通性判斷)
藍橋杯剪郵票(遞歸組合 + 連通性判斷)

請你計算,一共有多少種不同的剪取方法。

輸出

請填寫表示方案數目的整數。 

2. 思路分析:

① 因為要求必須剪的五張郵票數連接配接在一起的,是以根據這個特點我們可以知道可以使用dfs搜尋進行五個方塊的連通,一開始的想法是從目前的位置(i,j)出發,其中i,j表示二維坐标對應的位置,從目前的位置出發開始dfs搜尋,每搜尋一個位置那麼就标記目前的(i, j)位置但是後面發現有的情況是漏掉了的,比如從第三行的數字9開始,因為之前已經搜尋過第一行與第二行了是以這個時候以9開頭的郵票是不能夠剪成功的(最多存在9,10,11,12四塊)而比如以9開始的1,2,3,5,9就是一個合法的剪法,是以上面這樣标記是會忽略一些情況的,是以每一次從起點開始dfs搜尋結束之後标記目前二維坐标位置已經通路是錯誤的解法

藍橋杯剪郵票(遞歸組合 + 連通性判斷)

② 在網上看了一下具體的代碼,發現正确的解法是從12個數字中選擇出5個數字,然後記錄下選擇的5個數字,将5個數字映射到二維平面對應的坐标上,通過另外一個dfs2方法檢驗選擇的5個數字是否連通,思路其實還是很好了解的。總的來說是使用遞歸生成12選5的數字,可以使用遞歸的方法來生成12個數字選5個數字的組合方式,在生成5個數字的過程中需要使用一個清單記錄下這5個數字,并且将5個數字對應的坐标映射在二維平面上,然後再一次使用dfs檢查連通性,答案是116

3. 代碼如下:

組合 + 連通性判斷:

from typing import List

res = 0
# count用來記錄12選5個數字的下标
count = 0
# n表示
n = 0
A = [0] * 5
pos = [[0, 1], [0, -1], [-1, 0], [1, 0]]


# 檢驗5個數字在二維平面上的的連通性
def dfs2(x: int, y: int, B: List[List[int]]):
    global n
    for i in range(4):
        x0, y0 = x + pos[i][0], y + pos[i][1]
        if 0 <= x0 < 3 and 0 <= y0 < 4 and B[x0][y0] == 1:
            B[x0][y0] = 0
            n += 1
            dfs2(x0, y0, B)
            # 不知道為什麼回溯之後結果會不正确
            # B[x0][y0] = 1


def dfs(index: int):
    global count
    if count == 5:
        # print(A)
        # 注意在這裡需要使用global來修改全局變量如果沒有使用這個關鍵字會導緻修改不了
        global res, n
        x, y = 0, 0
        B = [[0] * 4 for i in range(3)]
        for i in range(5):
            x0, y0 = (A[i] - 1) // 4, (A[i] - 1) % 4
            if i < 4:
                # 标記矩陣中選擇5個數字的二維坐标
                B[x0][y0] = 1
            else:
                # 從最後一個坐标開始搜尋
                x, y = x0, y0
        n = 1
        dfs2(x, y, B)
        # 一開始的計數為1
        if n == 5:
            res += 1
    else:
        for i in range(index + 1, 13):
            A[count] = i
            count += 1
            dfs(i)
            count -= 1


if __name__ == '__main__':
    dfs(0)
    print(res)
           

錯誤代碼:

from typing import List

res = 0
pos = [[0, 1], [0, -1], [1, 0], [-1, 0]]


# rec用來記錄中間的遞歸結果看輸出結果是否正确
def dfs(x: int, y: int, count: int, vis: List[List[int]], rec: List[List[int]]):
    global res
    if count == 4:
        for i in range(len(rec)):
            print(rec[i])
        print()
        res += 1
    for i in range(4):
        x0, y0 = x + pos[i][0], y + pos[i][1]
        # 可以到達目前的位置
        if 0 <= x0 < 3 and 0 <= y0 < 4 and vis[x0][y0] == 0:
            rec[x0][y0] = 1
            vis[x0][y0] = 1
            dfs(x0, y0, count + 1, vis, rec)
            # 回溯
            rec[x0][y0] = 0
            vis[x0][y0] = 0


if __name__ == '__main__':
    vis = [[0] * 4 for i in range(3)]
    rec = [[0] * 4 for i in range(3)]
    for i in range(3):
        for j in range(4):
            rec[i][j] = 1
            vis[i][j] = 1
            dfs(i, j, 0, vis, rec)
            # 标記已經通路過的位置這樣可以避免重複計算某些剪法
            vis[i][j] = 1
            rec[i][j] = 0
    print(res)
           

繼續閱讀