Leetcode 1007:行相等的最少多米诺旋转(超详细的解法!!!)

在一排多米诺骨牌中,A[i]B[i] 分别代表第 i 个多米诺骨牌的上半部分和下半部分。(一个多米诺是两个从 1 到 6 的数字同列平铺形成的 —— 该平铺的每一半上都有一个数字。)

我们可以旋转第 i 张多米诺,使得 A[i]B[i] 的值交换。

返回能使 A 中所有值或者 B 中所有值都相同的最小旋转次数。

如果无法做到,返回 -1.

示例 1:

Leetcode 1007:行相等的最少多米诺旋转(超详细的解法!!!)_第1张图片
输入:A = [2,1,2,4,2,2], B = [5,2,6,2,3,2]
输出:2
解释:
图一表示:在我们旋转之前, A 和 B 给出的多米诺牌。
如果我们旋转第二个和第四个多米诺骨牌,我们可以使上面一行中的每个值都等于 2,如图二所示。

示例 2:

输入:A = [3,5,1,2,3], B = [3,6,3,3,4]
输出:-1
解释:
在这种情况下,不可能旋转多米诺牌使一行的值相等。

提示:

  1. 1 <= A[i], B[i] <= 6
  2. 2 <= A.length == B.length <= 20000

解题思路

首先,我们可以想到一个很朴素的思想,就是统计AB中最多出现的元素,然后再比较二者那个大,取其大者,最后判断其他元素不符的位置是否可以通过交换实现匹配。

from collections import Counter
class Solution:
    def minDominoRotations(self, A: List[int], B: List[int]) -> int:
        a, b = len(A), len(B)
        ca, cb = Counter(A), Counter(B)
        va, vb = ca.most_common(1), cb.most_common(1)
        res1, res2 = 0, 0
        
        if va[0][1] > vb[0][1]:
            for i in range(a):
                if A[i] != va[0][0]:
                    if B[i] == va[0][0]:
                        res1 += 1
                    else:
                        return -1
            return res1
        else:
            for i in range(b):
                if B[i] != vb[0][0]:
                    if A[i] == vb[0][0]:
                        res2 += 1
                    else:
                        return -1
            return res2
        return -1

非常幸运通过了测试,但是为什么这样做是对的呢?我举一个例子,如果A=[3,3,3,2,2],此时A中最多的元素是3,假设我们最后的结果不是3而是2,那么B中必然会有32,也就是此时的众数应该是2,这就是我们题设相违背了。所以我们推测出我们前面的想法应该是对的,但是具体的数学证明就不说了。

当然我们也可以通过暴力法,因为总共只有6个数。我们首先判断1~6这几个数是不是可以得到结果,然后从中取最下的反转次数即可。

class Solution:
    def minDominoRotations(self, A: List[int], B: List[int]) -> int:
        for i in range(1,7):
            if all(i == a or i == b for a,b in zip(A,B)):
                return  min(len(A)-A.count(i),len(B)-B.count(i))
            
        return -1

但是上面的算法都没有符合题目的本意,我们思考一个多米诺骨牌,无非就是将一个牌向后推的过程,所以我们这里只有两种情况,一种是A的首元素向后,另一种是B的是元素向后。

接着就是向后的过程,只有相同的元素我们的牌才会倒下,我们此时,只需通过两个遍历记录我们交替变更的次数(因为有两种情况,一种是A中的元素交换到B中,另一种是B中的元素交换到A中),最后去二者的最小值即可(如果能推到最后一个牌的话)。否则,GG!!!

class Solution:
    def minDominoRotations(self, A: List[int], B: List[int]) -> int:
        a, b, n = 0, 0, len(A)
        for i in range(n):
            if A[i] != A[0] and B[i] != A[0]:
                break
            if A[i] != A[0]:
                a += 1
            if B[i] != A[0]:
                b += 1
            if i == n - 1:
                return min(a, b)
            
        a, b = 0, 0
        for i in range(n):
            if A[i] != B[0] and B[i] != B[0]:
                break
            if A[i] != B[0]:
                a += 1
            if B[i] != B[0]:
                b += 1
            if i == n - 1:
                return min(a, b)
        return -1

reference:

https://leetcode.com/problems/minimum-domino-rotations-for-equal-row/submissions/

我将该问题的其他语言版本添加到了我的GitHub Leetcode

如有问题,希望大家指出!!!

你可能感兴趣的:(Problems,leetcode解题指南,leetcode)