An N x N board
contains only 0
s and 1
s. In each move, you can swap any 2 rows with each other, or any 2 columns with each other.
What is the minimum number of moves to transform the board into a "chessboard" - a board where no 0
s and no 1
s are 4-directionally adjacent? If the task is impossible, return -1.
Examples: Input: board = [[0,1,1,0],[0,1,1,0],[1,0,0,1],[1,0,0,1]] Output: 2 Explanation: One potential sequence of moves is shown below, from left to right: 0110 1010 1010 0110 --> 1010 --> 0101 1001 0101 1010 1001 0101 0101 The first move swaps the first and second column. The second move swaps the second and third row. Input: board = [[0, 1], [1, 0]] Output: 0 Explanation: Also note that the board with 0 in the top left corner, 01 10 is also a valid chessboard. Input: board = [[1, 0], [1, 0]] Output: -1 Explanation: No matter what sequence of moves you make, you cannot end with a valid chessboard.
Note:
board
will have the same number of rows and columns, a number in the range[2, 30]
.board[i][j]
will be only0
s or1
s.
這道題給了我們一個二維數組,里面都是由0和1組成的,讓我們通過交換行或者列來形成一個棋盤。棋盤我們都見過吧,就是國際象棋的那種棋盤,黑白相間的那種,用數組表示就是0和1交替出現,相鄰位置上的數字必定不是一樣的。這道題默認的棋盤的起始位置可以是1或者0,然后依次類推可得到所有位置上的值。這道題最大的難點是在於判斷給定的數組最終能否組成棋盤,因為能通過交換組成棋盤的數組其實是有很多苛刻條件需要滿足的,只有這些條件都滿足了,才能到計算交換數到那一步。首先我們先來看長度為4的棋盤:
1 0 1 0
0 1 0 1
1 0 1 0
0 1 0 1
或者:
0 1 0 1
1 0 1 0
0 1 0 1
1 0 1 0
我們發現對於長度為偶數的棋盤,每一行0和1的個數都是相等的,不管我們如何交換行和列,0和1的個數都是不會變化的,再看看長度為奇數的棋盤,比如3:
1 0 1
0 1 0
1 0 1
或者:
0 1 0
1 0 1
0 1 0
我們發現對於長度為奇數的棋盤,各行的0和1個數不同,但是還是有規律的,每行的1的個數要么為 n/2,要么為 (n+1)/2,這個規律一定要保證,不然無法形成棋盤。
還有一個很重要的規律,我們觀察題目給的第一個例子,如果我們只看行,我們發現只有兩種情況 0110 和 1001,如果只看列,只有 0011 和 1100,我們發現不管棋盤有多長,都只有兩種情況,而這兩種情況上各位上是相反的,只有這樣的矩陣才有可能轉換為棋盤。那么這個規律可以衍生出一個規律,就是任意一個矩形的四個頂點只有三種情況,要么四個0,要么四個1,要么兩個0兩個1,不會有其他的情況。那么四個頂點亦或在一起一定是0,所以我們判斷只要亦或出了1,一定是不對的,直接返回-1。之后我們來統計首行和首列中的1個數,因為我們要讓其滿足之前提到的規律。統計完了首行首列1的個數,我們判斷如果其小於 n/2 或者大於 (n+1) / 2,那么一定無法轉為棋盤。我們還需要算下首行和首列跟棋盤位置的錯位的個數,雖然 01010 和 10101 都可以是正確的棋盤,我們先默認跟 10101 比較好了,之后再做優化處理。
最后的難點就是計算最小的交換步數了,這里要分n的奇偶來討論。如果n是奇數,我們必須得到偶數個,為啥呢,因為我們之前統計的是跟棋盤位置的錯位的個數,而每次交換行或者列,會修改兩個錯位,所以如果是奇數就無法還原為棋盤。舉個例子,比如首行是 10001,如果我們跟棋盤 10101 比較,只有一個錯位,但是我們是無法通過交換得到 10101的,所以我們必須要交換得到 01010,此時的錯位是4個,而我們通過 n - rowDiff 正好也能得到4,這就是為啥我們需要偶數個錯位。如果n是偶數,那么就不會出現這種問題,但是會出現另一個問題,比如我們是 0101,這本身就是正確的棋盤排列了,但是由於我們默認是跟 1010 比較,那么我們會得到4個錯位,所以我們應該跟 n - rowDiff 比較取較小值。列的處理跟行的處理完全一樣。最終我們把行錯位個數跟列錯位個數相加,再除以2,就可以得到最小的交換次數了,之前說過了每交換一次,可以修復兩個錯位,參見代碼如下:
class Solution { public: int movesToChessboard(vector<vector<int>>& board) { int n = board.size(), rowSum = 0, colSum = 0, rowDiff = 0, colDiff = 0; for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { if (board[0][0] ^ board[i][0] ^ board[0][j] ^ board[i][j]) return -1; } } for (int i = 0; i < n; ++i) { rowSum += board[0][i]; colSum += board[i][0]; rowDiff += (board[i][0] == i % 2); colDiff += (board[0][i] == i % 2); } if (n / 2 > rowSum || rowSum > (n + 1) / 2) return -1; if (n / 2 > colSum || colSum > (n + 1) / 2) return -1; if (n % 2) { if (rowDiff % 2) rowDiff = n - rowDiff; if (colDiff % 2) colDiff = n - colDiff; } else { rowDiff = min(n - rowDiff, rowDiff); colDiff = min(n - colDiff, colDiff); } return (rowDiff + colDiff) / 2; } };
參考資料:
https://leetcode.com/problems/transform-to-chessboard/