并查集 题单 python (亲戚 修复公路 星球大战 Closing the Farm 村村通 洛谷)

因为并查集的特性导致它只能合并点而不能删点 所以有些题需要逆向来思考

每次增加一个点联通块增加一,然后与其相连的可访问的点进行并查集联通,若不属于同一个根,则进行联通,连通块减一

前两题比较简单,后面的需要逆向思考

P1111 修复公路

n,m=map(int,input().split())
la=[]
for i in range(m):
    a,b,c=map(int,input().split())
    la.append((a,b,c))
fa=[i for i in range(n+1)]#初始化
def find(x):
    if fa[x]==x:
        return x
    else:
        fa[x]=find(fa[x])
        return fa[x]

ans=0
mi=float("-inf")
la.sort(key=lambda x:x[2])
flag=0
for l in la:
    x=find(l[0])
    y=find(l[1])
    if x!=y:
        fa[x]=y
        ans=ans+1
        mi=l[2]
        if ans==n-1:
            flag=1
            break
if flag==1:
    print(mi)
else:
    print(-1)

P1551 亲戚

n,m,c=map(int,input().split())
fa=[i for i in range(n+1)]
def find(x):
    if fa[x]==x:
        return x
    else:
        fa[x]=find(fa[x])
        return fa[x]
for i in range(m):
    a,b=map(int,input().split())
    q=find(a)
    p=find(b)
    if q!=p:
        fa[q]=p
la=[]
for i in range(c):
    a,b=map(int,input().split())
    la.append((a,b))
for l in la:
    q = find(l[0])
    p = find(l[1])
    if q != p:
        print("No")
    else:
        print("Yes")

P1197 [JSOI2008] 星球大战

"""因为并查集的特性导致它只能合并点而不能删点 所以需要逆向来思考"""
n,m=map(int,input().split())#逆向思维,python 会超时,四个
e=[[] for i in range(n)]
for i in range(m):#建立双向边
    a,b=map(int,input().split())
    e[a].append(b)
    e[b].append(a)

k=int(input())
br=[]
vis=[0]*n
for i in range(k):#被破坏的星球,并标记
    c=int(input())
    br.append(c)
    vis[c]=1
cnt=n-k#没被破坏的星球
fa=[i for i in range(n)]#初始化
def find(x):#查找父亲节点
    if fa[x]==x:
        return x
    else:
        fa[x]=find(fa[x])
        return fa[x]
def bingcha1(u):#精髓
    global cnt
    for i in e[u]:
        if vis[i]:#被破坏的星球,不进行联通
            continue
        x=find(u)
        y=find(i)
        if x!=y:
            fa[x]=y
            cnt=cnt-1#连通块减一
def bingcha2(u):
    global cnt
    if vis[u]:#将被破坏的星球,变成未破坏的,加上一个点,联通块加一
        cnt=cnt+1
        vis[u]=0
    for i in e[u]:
        if vis[i]:#被破坏的星球,不进行联通
            continue
        x=find(u)
        y=find(i)
        if x!=y:
            fa[x]=y
            cnt=cnt-1#连通块减一

for i in range(n):
    if not vis[i]:bingcha1(i)#没被破坏的星球,进行并查集
res=[]#结果集
res.append(cnt)
for i in range(k-1,-1,-1):
    bingcha2(br[i])
    res.append(cnt)
for k in res[::-1]:
    print(k)

P3144 [USACO16OPEN]Closing the Farm S

"""将无法访问的点标记,每次增加一个点联通块增加一,然后与其相连的可访问的点进行并查集
联通,若不属于同一个根,则进行联通,连通块减一"""
n,m=map(int,input().split())#要用逆向思维,逐个点加进去
e=[[] for i in range(n+1)]
for i in range(m):#存储
    a,b=map(int,input().split())
    e[a].append(b)
    e[b].append(a)
vis=[0]*(n+1)
lc=[]
for i in range(n):
    c=int(input())
    vis[c]=1
    lc.append(c)
fa=[i for i in range(n+1)]

def find(x):
    if fa[x]==x:
        return x
    else:
        fa[x]=find(fa[x])
        return fa[x]
def liantong(v):
    global cnt
    cnt=cnt+1#加上一个点,连通块加上一
    vis[v]=0#将其标记为可访问
    for i in e[v]:#与其相连,且可访问的点进行并查集,
        if vis[i]==1:
            continue
        x=find(v)
        y=find(i)
        if x!=y:#若不属于同根,则用其根节点指向另一个根节点,并将连通块个数减一
            fa[x]=y
            cnt=cnt-1

cnt=1
vis[lc[-1]]=0
lc.pop()#首先处理倒数第一个数
jieguo=["YES"]
for v in lc[::-1]:
    liantong(v)
    if cnt==1:
        jieguo.append("YES")
    else:
        jieguo.append("NO")

for i in jieguo[::-1]:#逆向输出
    print(i)

P1536 村村通

"""并查集形成的是树,树的性质n个节点,n-1边,两两节点相互联通,由性质推每次find(a)!=find(b)时形成一条边
答案即是n-1-cnt,cnt是形成的边数"""
res=[]#答案
while True:
    l=list((map(int,input().split())))
    if len(l)==1:
        break
    fa = [i for i in range(l[0]+1)]
    def find(x):
        if x==fa[x]:
            return x
        else:
            fa[x]=find(fa[x])
            return fa[x]
    cnt=0
    for i in range(l[1]):
        lw=list(map(int,input().split()))
        c=find(lw[0])
        d=find(lw[1])
        if c!=d:
            fa[c]=d
            cnt=cnt+1
    res.append(l[0]-1-cnt)
for i in res:
    print(i)

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值