求连通块问题

emmmm,对于连通块,其实有很多方式求解。这里我还是以那个剪邮票的题目为例子来简述怎么用python操作。

主要思想

遍历一遍某点的周围连通的块

遍历给出的块

判断它们是不是相等的,打标记,相等就连通块个数+1

再遍历给出块中剩下的块

如果都相等,连通块个数=给出的块的个数

DFS判断是否是连通块

一般来说,DFS判断连通块无非就是从一个点开始往它的四面八方(上下左右)走,能走到的地方属于该点的连通块集合,判断给出的数组的其他点在不在这个集合里面就行了。

在这个剪邮票的题目中原始的数组为mp=[1,2,3,4,5,6,7,8,9,10,11,12](因为是用排列组合求出的可能存在的连通块,所以这里不是用坐标传入dfs,而是用数值传入dfs),同一行的相邻数字相差1(左右走),同一列的相邻数字相差4(上下走)。

这是原始的邮票

但是,很显然5和9不满足这一点,如果5-1那就走到4那边去了,就不是我们希望的上下走,9同理。

为了克服这一点,我们把数组改成mp=[1,2,3,4,6,7,8,9,11,12,13,14]

邮票集变成:

1234
6789
11121314

这样在边界处的6±1就只会往5和7的方向走,到时候直接把5略去就好。

修正后的方向集为d=[1,-1,5,-5]代表上下左右四个方向。

mp=[1,2,3,4,6,7,8,9,11,12,13,14]
cut=[0]*5#剪下的数字
vis=[0]*5#标记数组
#用来记录总共可
tot=0
def dfs(u):
    #往上下左右走
    d=[-1,1,-5,5]
    for i in range(4):
        #上下左右走就是加减数字
        x=cut[u]+d[i]
        if x<1 or x>15 or x==5 or x==10:
            continue
        for j in range(5):
            if vis[j]==0 and cut[j]==x:
                vis[j]=1
                dfs(j)
for a in range(12):
    for b in range(a+1,12):
        for c in range(b+1,12):
            for d in range(c+1,12):
                for e in range(d+1,12):
                    cut[0]=mp[a]
                    cut[1]=mp[b]
                    cut[2]=mp[c]
                    cut[3]=mp[d]
                    cut[4]=mp[e]
                    for i in range(5):
                        vis[i]=0
                    vis[0]=1
                    dfs(0)
                    length=0
                    for i in range(5):
                        if vis[i]==1:  
                            length+=1
                    if length==5:
                        tot+=1
print(tot)

BFS判断连通块

from queue import *
mp=[1,2,3,4,6,7,8,9,11,12,13,14]
#用来记录总共可
tot=0
#往上下左右走
d=[-1,1,-5,5]
def bfs():
    vis=[0]*5#标记数组
    p=0
    q=Queue()
    q.put(0)
    vis[0]=1
    while not q.empty():
        i=q.get()
        p+=1
        for j in range(5):
            if vis[j]==0:
                for k in range(4):
                    if mp[i]+d[k]==mp[j]:
                        q.put(j)
                        vis[j]=1
    if p==5:
        return True
    else:
        return False

def perm(st,ed):
    global tot
    if st==5:
        if bfs():
            tot+=1
    else:
        for i in range(st,ed+1):#注意这里是ed+1
            mp[st],mp[i]=mp[i],mp[st]
            perm(st+1,ed)
            mp[st],mp[i]=mp[i],mp[st]
perm(0,11)
print(tot//120)

用并查集判断连通块

mp=[1,2,3,4,6,7,8,9,11,12,13,14]
num=0
d=[1,-1,5,-5]
def check():
    #初始化并查集,把5个数初始化为5个集
    #集等于不同的数字,表示不连通
    set=[0,1,2,3,4]
    for i in range(5):
        for j in range(5):
            for k in range(4):
                if mp[i]+d[k]==mp[j]:
                    j_set=set[j]
                    #把ij连通
                    set[j]=set[i]
                    for v in range(5):
                        if set[v]==j_set:
                            #和j连通的v和i也连通
                            set[v]=set[i]
    #交错对比第一个元素和第二个元素,第二个元素和第三个元素……是否相等
    if set[1:]==set[:-1]:
        return True
def perm(st,ed):
    global num
    if st==5:
        if check():
            num+=1
        else:
            for i in range(st,ed):
                mp[i],mp[st]=mp[st],mp[i]
                perm(st+1,ed)
                mp[i],mp[st]=mp[st],mp[i]
perm(0,11)
print(num//120)
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值