Skip to content

LeetCode: 1007-Minimum Domino Rotations For Equal Row 解題紀錄

Last Updated on 2022-03-20 by Clay

題目

In a row of dominoes, tops[i] and bottoms[i] represent the top and bottom halves of the ith domino. (A domino is a tile with two numbers from 1 to 6 - one on each half of the tile.)

We may rotate the ith domino, so that tops[i] and bottoms[i] swap values.

Return the minimum number of rotations so that all the values in tops are the same, or all the values in bottoms are the same.

If it cannot be done, return -1.

Example 1:

Input: tops = [2,1,2,4,2,2], bottoms = [5,2,6,2,3,2]
Output: 2
Explanation: 
The first figure represents the dominoes as given by tops and bottoms: before we do any rotations.
If we rotate the second and fourth dominoes, we can make every value in the top row equal to 2, as indicated by the second figure.

Example 2:

Input: tops = [3,5,1,2,3], bottoms = [3,6,3,3,4]
Output: -1
Explanation: 
In this case, it is not possible to rotate the dominoes to make one row of values equal.

Constraints:

  • 2 <= tops.length <= 2 * 104
  • bottoms.length == tops.length
  • 1 <= tops[i], bottoms[i] <= 6

題目會給 topsbottoms 兩個列表,列表中儲存的數字代表骰子的點數;我們要做的,就是看如何交換才能以最少的步驟讓其中一個列表的點數全部相同。

如果不存在可以讓列表點數相同的方法,則回傳 -1。


解題思路

這題我真的沒有想到特別好的解法,只有一個 brute force 的暴力解、以及優化一點這個暴力解的寫法。

如過有更好的解法,歡迎隨時提供給我!非常感謝!


Brute Force

首先,把所有點數都試試看能否排成同一列表全部相同,與此同時紀錄需要交換骰子點數的次數;除了 tops 需要計算外、也需要把 bottoms 計算一次。

我在程式碼中使用了 hash map 來記錄該點數是否已經被計算過,計算過的點數可以跳過;不過由於骰子只存在六種點數、十分固定,所以空間複雜度我直接算 O(1)。

當然,正是因為程式碼實在太長了,所以我還是想辦法優化了程式碼的行數與可讀性。



Brute Force 複雜度

Time complexityO(n)
Space complexityO(1)


Brute Force C++ 範例程式碼

class Solution {
public:
    int minDominoRotations(vector<int>& tops, vector<int>& bottoms) {
        // Init
        int minTried = INT_MAX;
        unordered_map<int, int> used;
        
        // Find the mini-steps method
        for (int prob: tops) {
            int temp = 0;
            bool isBreak = false;
            
            if (used.count(prob)) continue;
            else used[prob] = 1;
            
            for (int i=0; i<tops.size(); ++i) {
                if (tops[i] != prob) {
                    // If bottom can exchange, increase the `temp` variable
                    if (bottoms[i] == prob) {
                        ++temp;
                    }
                    
                    // If top and bottom is not equal to prob[i], break out the matching
                    else {
                        isBreak = true;
                        break;
                    }
                }
            }
            
            if (!isBreak) {
                // Maybe we rotate bottoms is faster than tops
                temp = min(temp, int(tops.size()-temp));
                
                // Minimum
                minTried = min(minTried, temp);
            }
        }
        
        // Use Bottom to calculation
        used.clear();
        
        // Find the mini-steps method
        for (int prob: bottoms) {
            int temp = 0;
            bool isBreak = false;
            
            if (used.count(prob)) continue;
            else used[prob] = 1;
            
            for (int i=0; i<bottoms.size(); ++i) {
                if (bottoms[i] != prob) {
                    // If bottom can exchange, increase the `temp` variable
                    if (tops[i] == prob) {
                        ++temp;
                    }
                    
                    // If top and bottom is not equal to prob[i], break out the matching
                    else {
                        isBreak = true;
                        break;
                    }
                }
            }
            
            if (!isBreak) {
                // Maybe we rotate bottoms is faster than tops
                temp = min(temp, int(tops.size()-temp));
                
                // Minimum
                minTried = min(minTried, temp);
            }
        }
        
        // If the minTried is still `INT_MAX`, that means it cannot be done
        if (minTried == INT_MAX) return -1;
        else return minTried;
    }
};



Brute Force Python 範例程式碼

class Solution:
    def minDominoRotations(self, tops: List[int], bottoms: List[int]) -> int:
        # Init
        min_tried = 20001
        used = dict()
        
        # Find the mini-steps method
        for prob in tops:
            temp = 0
            isBreak = False
            
            if used.get(prob, 0) == 1: continue
            else: used[prob] = 1
            
            for i in range(len(tops)):
                if tops[i] != prob:
                    # If bottom can exchange, increase the `temp` variable
                    if bottoms[i] == prob:
                        temp += 1
                    
                    # If top and bottom is not equal to prob[i], break out the matching
                    else: 
                        isBreak = True
                        break
            
            if not isBreak:
                min_tried = min(min_tried, temp)

        # Use Bottom to calculation
        used.clear()
        
        # Find the mini-steps method
        for prob in bottoms:
            temp = 0
            isBreak = False
            
            if used.get(prob, 0) == 1: continue
            else: used[prob] = 1
            
            for i in range(len(bottoms)):
                if bottoms[i] != prob:
                    # If tops can exchange, increase the `temp` variable
                    if tops[i] == prob:
                        temp += 1
                    
                    # If top and bottom is not equal to prob[i], break out the matching
                    else: 
                        isBreak = True
                        break
            
            if not isBreak:
                min_tried = min(min_tried, temp)
        
        # If the minTried is still `INT_MAX`, that means it cannot be done
        return -1 if min_tried == 20001 else min_tried



優化解法

直接記算兩列表中的不同點數出現次數、以及兩列表剛好出現同個點數的次數。因為這樣一來,我們可以透過相加兩列表中不同點數出現次數再減去剛好出現相同的次數是否等於列表長度,來判斷是否可以完成交換。

有點繞口,不過簡單來說,假設 AB 列表一共有 6 個點數,其中 2 的點數 A 出現了 4 次,B 出現了 3 次,而 AB 剛好都為 2 的次數有 1 次。

A[2] = 4
B[2] = 3
SAME[2] = 1

4 + 3 - 1 = 6 (是可以交換成列表中都是同樣點數的!)

接著就是跟第一種解法一樣,紀錄最短的交換次數即可。

其實跟第一種解法非常相似,只是多了紀錄出現相同點數的次數這個步驟,讓本來需要判斷兩次的程式碼簡化成了一次。

優化解法複雜度

同樣,只使用了固定長度為 6 的列表儲存,同樣視為空間複雜度 O(1)。

Time complexityO(n)
Space complexityO(1)


優化解法 C++ 範例程式碼

class Solution {
public:
    int minDominoRotations(vector<int>& tops, vector<int>& bottoms) {
        // Init
        int minRotation = INT_MAX;
        vector<int> topNums(6, 0);
        vector<int> bottomNums(6, 0);
        vector<int> sameNums(6, 0);
        
        // Count the Domino numbers
        for (int i=0; i<tops.size(); ++i) {
            ++topNums[tops[i]-1];
            ++bottomNums[bottoms[i]-1];
            
            if (tops[i] == bottoms[i]) {
                ++sameNums[tops[i]-1];
            }
        }
        
        // Find the minimum numbers of rotations
        for (int i=0; i<6; ++i) {
            if (topNums[i]+bottomNums[i]-sameNums[i] == tops.size()) {
                int temp = min(topNums[i]-sameNums[i], bottomNums[i]-sameNums[i]);
                minRotation = min(minRotation, temp);
            }
        }
        
        // If minRotation is equal to `INT_MAX`, it means it cannot be done
        return minRotation == INT_MAX ? -1 : minRotation;
    }
};



優化解法 Python 範例程式碼

class Solution:
    def minDominoRotations(self, tops: List[int], bottoms: List[int]) -> int:
        # Init
        minRotation = 20001
        topNums = [0 for _ in range(6)]
        bottomNums = [0 for _ in range(6)]
        sameNums = [0 for _ in range(6)]
        
        # Count the Domino numbers
        for i in range(len(tops)):
            topNums[tops[i]-1] += 1
            bottomNums[bottoms[i]-1] += 1
            
            if tops[i] == bottoms[i]:
                sameNums[tops[i]-1] += 1
        
        # Find the minimum numbers of rotations
        for i in range(6):
            if topNums[i]+bottomNums[i]-sameNums[i] == len(tops):
                temp = min(topNums[i]-sameNums[i], bottomNums[i]-sameNums[i])
                minRotation = min(minRotation, temp)
        
        # If minRotation is equal to `INT_MAX`, it means it cannot be done
        return -1 if minRotation == 20001 else minRotation;

References


Read More

Leave a Reply