223周赛 (快慢指针,并查集的操作(重复需删除),状态压缩dp)

1720. 解码异或后的数组

整数数组 arr 由 n 个非负整数组成。

经编码后变为长度为 n - 1 的另一个整数数组 encoded ,其中 encoded[i] = arr[i] XOR arr[i + 1] 。例如,arr = [1,0,2,1] 经编码后得到 encoded = [1,2,3] 。

给你编码后的数组 encoded 和原数组 arr 的第一个元素 first(arr[0])。

请解码返回原数组 arr 。可以证明答案存在并且是唯一的。

示例 1:

输入:encoded = [1,2,3], first = 1
输出:[1,0,2,1]
解释:若 arr = [1,0,2,1] ,那么 first = 1 且 encoded = [1 XOR 0, 0 XOR 2, 2 XOR 1] = [1,2,3]

示例 2:

输入:encoded = [6,2,7,3], first = 4
输出:[4,2,0,7,4]

提示:

2 <= n <= 104
encoded.length == n - 1
0 <= encoded[i] <= 10 ^ 5
0 <= first <= 10 ^ 5

Note

  1. 异或的性质:若a^ b=c,则a^c=b

Code

class Solution:
    def decode(self, encoded: List[int], first: int) -> List[int]:
        if(not encoded): return []
        temp = first
        res = [first]
        for i in range(len(encoded)):
            x = temp ^ encoded[i]
            res.append(x)
            temp = x
        return res

1721. 交换链表中的节点

给你链表的头节点 head 和一个整数 k 。

交换 链表正数第 k 个节点和倒数第 k 个节点的值后,返回链表的头节点(链表 从 1 开始索引)。

223周赛 (快慢指针,并查集的操作(重复需删除),状态压缩dp)_第1张图片

示例 1:

输入:head = [1,2,3,4,5], k = 2
输出:[1,4,3,2,5]

示例 2:

输入:head = [7,9,6,6,7,8,3,0,9,5], k = 5
输出:[7,9,6,6,8,7,3,0,9,5]

示例 3:

输入:head = [1], k = 1
输出:[1]

示例 4:

输入:head = [1,2], k = 1
输出:[2,1]

示例 5:

输入:head = [1,2,3], k = 2
输出:[1,2,3]

提示:

链表中节点的数目是 n
1 <= k <= n <= 105
0 <= Node.val <= 100

Note

  1. 快慢指针题目,第一次递交的时候偷懒,利用pat的排序,直接a掉,比赛的时候想着再练练指针的操作,就把要将换的两个节点直接换掉了(不仅仅只换值),导致各种错误,边界考虑不清

Code1 pat排序版

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def swapNodes(self, head: ListNode, k: int) -> ListNode:
        temp = head
        res = []
        while(temp):
            res.append(temp.val)
            temp = temp.next
      
        k = k
        tempa = res[k-1]
        res[k-1] = res[-k]
        res[-k] = tempa
        cnt = 0
        temp = head
        while(temp):
            temp.val = res[cnt]
            cnt += 1
            temp = temp.next
        return head

Note2

1.边界情况需要注意的有

  • first > second
  • first == second
  • first 和 second为第一个和最后一个节点

Code2 交换节点版

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def swapNodes(self, head: ListNode, k: int) -> ListNode:
        temp = head
        cnt = 1
        second = None
        if(not head or head.next == None): return head
        if(k == 1): 
            first = head
        while(temp):
            if(second): 
                second = second.next
            if(cnt + 1 == k):
                first = temp
            if(cnt - 1 == k):
                second = head
            temp = temp.next
            cnt += 1
        # 若前后指针错位
        if(k + 1 > cnt // 2):
            t = first
            first = second
            second = t
        # 若需操作节点为第一个
        if(k == 1 or k + 1== cnt):
            tempa = head.next
            tempb = second.next
            head.next = None
            if(tempa != tempb):
                tempb.next = tempa
                second.next = head
            else:
                tempb.next = head
            head = tempb
            return head
        # 正常操作
        if(first != second):
            tempa = first.next
            tempb = second.next
            first.next = second.next
            second.next = tempa
            tempc = tempa.next
            tempa.next = tempb.next
            tempb.next = tempc
        return head

Code3 正常版

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def swapNodes(self, head: ListNode, k: int) -> ListNode:
        p,q,n=head,head,head
        i=1
        while n:
            if ik:
                q=q.next#倒数第k个
            n=n.next
            i+=1
        p.val,q.val=q.val,p.val
        return head


1722. 执行交换操作后的最小汉明距离

给你两个整数数组 source 和 target ,长度都是 n 。还有一个数组 allowedSwaps ,其中每个 allowedSwaps[i] = [ai, bi] 表示你可以交换数组 source 中下标为 ai 和 bi(下标从 0 开始)的两个元素。注意,你可以按 任意 顺序 多次 交换一对特定下标指向的元素。

相同长度的两个数组 source 和 target 间的 汉明距离 是元素不同的下标数量。形式上,其值等于满足 source[i] != target[i] (下标从 0 开始)的下标 i(0 <= i <= n-1)的数量。

在对数组 source 执行 任意 数量的交换操作后,返回 source 和 target 间的 最小汉明距离 。

示例 1:

输入:source = [1,2,3,4], target = [2,1,4,5], allowedSwaps = [[0,1],[2,3]]
输出:1
解释:source 可以按下述方式转换:

  • 交换下标 0 和 1 指向的元素:source = [2,1,3,4]
  • 交换下标 2 和 3 指向的元素:source = [2,1,4,3]
    source 和 target 间的汉明距离是 1 ,二者有 1 处元素不同,在下标 3 。

示例 2:

输入:source = [1,2,3,4], target = [1,3,2,4], allowedSwaps = []
输出:2
解释:不能对 source 执行交换操作。
source 和 target 间的汉明距离是 2 ,二者有 2 处元素不同,在下标 1 和下标 2 。

示例 3:

输入:source = [5,1,2,4,3], target = [1,5,4,2,3], allowedSwaps = [[0,4],[4,2],[1,3],[1,4]]
输出:0

提示:

  • n == source.length == target.length
  • 1 <= n <= 105
  • 1 <= source[i], target[i] <= 105
  • 0 <= allowedSwaps.length <= 105
  • allowedSwaps[i].length == 2
  • 0 <= ai, bi <= n - 1
  • ai != bi

Note

  1. 比赛的时候想到利用并查集,很快啊,模板一套,就卡在了一个不知道为啥会错的数据上,手动模拟了半天都不知道为啥错了,后面被告知需区分重复的情况,这才明白自己思路上的问题。以下是错误的样例,target数组中有两个33,然而直接套用并查集的模板,是发现不了第二个33是无法被操作的223周赛 (快慢指针,并查集的操作(重复需删除),状态压缩dp)_第2张图片
  2. 鉴于此,首先考虑在并查集的模板上实现删除的操作,但是想要妥当的删除并查集中的一个节点,需要遍历并查集中的每一个节点,时间复杂度多了一个量级,10的5次方数据量,显然不允许o(n^2)的算法
  3. 利用map记录每个并查集根所管理的元素下标,统计target数组中,这些元素的出现次数,每匹配一次,计数减一,若计数为零,即target(输入数组2)中不存在source(输入数组1)下标对应的元素(或已经被删除),则ans++
  4. 注意,map(记录每个并查集根所管理的元素下标)在遍历的时候已经将根纳入到value中,因此遍历map也就遍历且仅遍历了一次source(输入数组1)

Code1

class UnionFind:
    def __init__(self, l):
        self.father =  {i:i for i in range(l)}
    def find(self,x):
        root = x
        while self.father[root] != root:
            root = self.father[root]
        # 路径压缩
        while x != root:
            original_father = self.father[x]
            self.father[x] = root
            x = original_father
        
        return root
    
    def union(self,x,y):
        root_x,root_y = self.find(x),self.find(y)
        if root_x != root_y:
            self.father[root_x] = root_y
    def is_connected(self,x,y):
        return self.find(x) == self.find(y)
    
class Solution:
    def minimumHammingDistance(self, source: List[int], target: List[int], allowedSwaps: List[List[int]]) -> int:
        temp = UnionFind(len(source))
        for i in range(len(allowedSwaps)):
            temp.union(allowedSwaps[i][0], allowedSwaps[i][1])
        dic = defaultdict(list)
        for i in range(len(source)):
            a = temp.find(i)        
            dic[a].append(i)
        res = 0

        for k, v in dic.items():
            print(k)
            print(v)
            #print(k)
            a=[source[i] for i in v]
            b=Counter([target[i] for i in v])
            for c in a:
                if b[c]>0:
                    b[c]-=1
                else:
                    res+=1
        return res

并查集模板:

class UnionFind:
    def __init__(self, l):
        self.father =  {i:i for i in range(l)}
    def find(self,x):
        root = x
        while self.father[root] != root:
            root = self.father[root]
        # 路径压缩
        while x != root:
            original_father = self.father[x]
            self.father[x] = root
            x = original_father
        
        return root
    
    def union(self,x,y):
        root_x,root_y = self.find(x),self.find(y)
        if root_x != root_y:
            self.father[root_x] = root_y
    def is_connected(self,x,y):
        return self.find(x) == self.find(y)

1723. 完成所有工作的最短时间

给你一个整数数组 jobs ,其中 jobs[i] 是完成第 i 项工作要花费的时间。

请你将这些工作分配给 k 位工人。所有工作都应该分配给工人,且每项工作只能分配给一位工人。工人的 工作时间 是完成分配给他们的所有工作花费时间的总和。请你设计一套最佳的工作分配方案,使工人的 最大工作时间 得以 最小化 。

返回分配方案中尽可能 最小 的 最大工作时间 。

示例 1:

输入:jobs = [3,2,3], k = 3
输出:3
解释:给每位工人分配一项工作,最大工作时间是 3 。

示例 2:

输入:jobs = [1,2,4,7,8], k = 2
输出:11
解释:按下述方式分配工作:
1 号工人:1、2、8(工作时间 = 1 + 2 + 8 = 11)
2 号工人:4、7(工作时间 = 4 + 7 = 11)
最大工作时间是 11 。

提示:

1 <= k <= jobs.length <= 12
1 <= jobs[i] <= 10^7

Note1:

  1. 状态压缩dp入门:(形式化方法真的有用)https://zhuanlan.zhihu.com/p/131585177#%E6%80%BB%E7%BB%93223周赛 (快慢指针,并查集的操作(重复需删除),状态压缩dp)_第3张图片
  2. 题目分析:
    223周赛 (快慢指针,并查集的操作(重复需删除),状态压缩dp)_第4张图片

Code1(C++AC,PythonTLE)

class Solution:
    def minimumTimeRequired(self, jobs: List[int], k: int) -> int:
        l = len(jobs)
        bit = [0] * (1 << l)      
        for i in range(1, 1 << l):
            for j in range (l):
                if(i & (1 << j) == 0): continue
                temp = i - (1 << j)
                bit[i] = bit[temp] + jobs[j]
                break
        dp = [[-1] * (1 << l) for _ in range(k)]
        for i in range(1 << l):
            dp[0][i] = bit[i]
        for i in range(1, k):
            for j in range(1 << l):
                s = j
                MIN_VAL = sys.maxsize
                while s :
                    val = max(dp[i - 1][j - s], bit[s])
                    MIN_VAL = min(val, MIN_VAL)
                    s = (s - 1) & j 
                dp[i][j] = MIN_VAL
        return dp[k-1][(1 << l) - 1]

总结

  1. 链表操作特别关注头结点和尾节点
  2. 并查集的模板加入初始化, 注意模型转换时特殊情况的考虑
  3. 状态压缩dp核心便是用二进制表示当前的状态

你可能感兴趣的:(LeetCode)