4084 号码牌(并查集)

本文介绍了一种利用并查集的数据结构来解决关于小朋友交换号码牌的问题。通过分析题目的规则,发现交换的连通性特征,利用并查集进行集合合并,判断是否能实现每个小朋友拿到对应编号的号码牌。关键在于理解交换的传递性和并查集的合并操作。
摘要由CSDN通过智能技术生成

1. 问题描述:

有 n 个小朋友,编号 1∼n。每个小朋友都拿着一个号码牌,初始时,每个小朋友拿的号码牌上的号码都等于其编号。每个小朋友都有一个幸运数字,第 i 个小朋友的幸运数字为 di。对于第 i 个小朋友,他可以向第 j 个小朋友发起交换号码牌的请求,当且仅当 |i − j| = di 成立。注意,请求一旦发出,对方无法拒绝,只能立刻进行交换。每个小朋友都可以在任意时刻发起任意多次交换请求。给定一个 1∼n 的排列 a1,a2,…,an。请问,通过小朋友相互之间交换号码牌,能否使得第 i 个小朋友拿的号码牌上的号码恰好为 ai,对 i∈[1,n] 均成立。

输入格式

第一行包含整数 n。第二行包含 n 个整数 a1,a2,…,an。第三行包含 n 个整数 d1,d2,…,dn。

输出格式

共一行,如果能做到,则输出 YES,否则输出 NO。

数据范围

前 6 个测试点满足 1 ≤ n ≤ 10。
所有测试点满足 1 ≤ n ≤ 100,1 ≤ di ≤ n,保证 a1∼an 是一个 1∼n 的排列。

输入样例1:

5
5 4 3 2 1
1 1 1 1 1

输出样例1:

YES

输入样例2:

7
4 3 5 1 2 7 6
4 6 6 1 6 6 1

输出样例2:

NO

输入样例3:

7
4 2 5 1 3 7 6
4 6 6 1 6 6 1

输出样例3:

YES
来源:https://www.acwing.com/problem/content/4087/

2. 思路分析:

分析题目可以知道每一个小朋友在满足可以交换的条件下可以进行交换的操作,并且可以发现交换具有传递性,例如A可以和B交换,B可以和C交换,那么A可以和C交换,进而A,B,C之间可以任意交换,相互交互在图论上属于一个连通块,相当于一棵树的形式,也即在同一个集合中的所有元素是可以任意交换的,不在同一个集合中的元素不能够交换,根据这个特点可以知道我们需要合并同一个集合中的所有元素,而合并集合中的元素可以使用并查集进行合并,所以这道题目考察的是并查集的相关操作,由于一开始的时候i = ai,所以我们可以从1枚举到n,将当前节点合并到它所在的集合中,声明A,B分别记录集合中已经有牌的集合和需要的牌的集合,从1枚举到n,枚举的过程中通过并查集的find操作找到当前的节点i所在的集合,当前节点i所在的集合A[find(i)]加上当前的节点i,当前节点i所在的集合B[find(i)]加上a[i]表示当前集合需要a[i]这个元素,最终判断A和B中每一个集合的元素是否相同即可。

3. 代码如下:

from typing import List


class Solution:
    def find(self, x: int, fa: List[int]):
        if x != fa[x]: fa[x] = self.find(fa[x], fa)
        return fa[x]

    def merge(self, a: int, b: int, n: int, fa: List[int]):
        # 越界了所以需要跳过
        if b < 1 or b > n: return
        # 合并a, b两个节点为同一个集合
        fa[self.find(a, fa)] = self.find(b, fa)

    # 并查集
    def process(self):
        n = int(input())
        a = [0] + list(map(int, input().split()))
        d = [0] + list(map(int, input().split()))
        fa = [i for i in range(n + 10)]
        for i in range(1, n + 1):
            # 合并属于同一个集合中的所有点
            self.merge(i, i - d[i], n, fa)
            self.merge(i, i + d[i], n, fa)
        # A为当前已有牌的集合, B为需要的牌的集合
        A, B = [list() for i in range(n + 10)], [list() for i in range(n + 10)]
        for i in range(1, n + 1):
            # 合并每一个节点属于哪一个集合
            A[self.find(i, fa)].append(i)
            B[self.find(i, fa)].append(a[i])
        # A是有序的但是B不是有序的所以需要对每一个B[i]进行排序
        for i in range(1, n + 1):
            if B[i]:
                # 从小到大进行排序
                B[i].sort()
        # 判断两个集合是否相等, python直接判断相等即可
        for i in range(1, n + 1):
            if A[i] != B[i]:
                print("NO")
                return
        print("YES")


if __name__ == "__main__":
    Solution().process()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值