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.html
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 only 0
s or 1
s.
這道題給了咱們一個二維數組,裏面都是由0和1組成的,讓咱們經過交換行或者列來造成一個棋盤。棋盤咱們都見過吧,就是國際象棋的那種棋盤,黑白相間的那種,用數組表示就是0和1交替出現,相鄰位置上的數字一定不是同樣的。這道題默認的棋盤的起始位置能夠是1或者0,而後依次類推可獲得全部位置上的值。這道題最大的難點是在於判斷給定的數組最終可否組成棋盤,由於能經過交換組成棋盤的數組實際上是有不少苛刻條件須要知足的,只有這些條件都知足了,才能到計算交換數到那一步。首先咱們先來看長度爲4的棋盤:spa
1 0 1 0code
0 1 0 1orm
1 0 1 0htm
0 1 0 1blog
或者:ci
0 1 0 1leetcode
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/