Hard
题目描述
给你一个 n x n 的二进制网格 board。在每一次移动中,你可以交换任意两行之间的位置,或者交换任意两列之间的位置。
返回将此数组变为"棋盘"所需的最小移动次数。如果不存在可行的变换,输出 -1。
“棋盘"是指黑白格相间的数组,即任意一个数字的上下左右四个方向的数字都与该数字不同。
示例 1:
输入: board = [[0,1,1,0],[0,1,1,0],[1,0,0,1],[1,0,0,1]]
输出: 2
解释: 一个可行的变换序列是第一行图向第二行图的变化。
第一次移动交换了第一和第二列。
第二次移动交换了第二和第三行。
示例 2:
输入: board = [[0,1],[1,0]]
输出: 0
解释: 注意左上角的格值为0也是一种可行的棋盘,也是合法的。
示例 3:
输入: board = [[1,0],[1,0]]
输出: -1
解释: 任意的变换都无法变为合法的棋盘。
提示:
- n == board.length
- n == board[i].length
- 2 <= n <= 30
- board[i][j] 是 0 或 1
解题思路
解题思路
棋盘变换问题的关键在于理解棋盘的性质和变换规律。
核心观察:
- 在一个合法的棋盘中,只能存在两种不同的行模式和两种不同的列模式
- 这两种模式必须是互补的(如
01010...和10101...) - 每种模式的数量差不能超过1
算法步骤:
- 验证可行性:检查是否只有两种行模式和两种列模式,且它们互为补集
- 计算最小移动次数:
- 对于行:计算当前排列到目标排列的最小交换次数
- 对于列:同样计算最小交换次数
- 如果 n 是偶数,有两种可能的目标模式,选择移动次数较小的
- 如果 n 是奇数,目标模式唯一确定(较多的模式必须在偶数位置)
关键点:
- 交换相邻两个错位元素的最小次数 = 错位元素个数 / 2
- 需要分别处理奇偶大小的棋盘,因为它们的约束条件不同
这个问题结合了位操作、数学推理和贪心算法的思想,是一道综合性较强的题目。
代码实现
class Solution {
public:
int movesToChessboard(vector<vector<int>>& board) {
int n = board.size();
// Check if transformation is possible
// There should be exactly 2 kinds of rows and 2 kinds of columns
map<vector<int>, int> rowCount, colCount;
for (int i = 0; i < n; i++) {
rowCount[board[i]]++;
vector<int> col;
for (int j = 0; j < n; j++) {
col.push_back(board[j][i]);
}
colCount[col]++;
}
if (rowCount.size() != 2 || colCount.size() != 2) return -1;
// Check if the two row patterns are complementary
vector<vector<int>> rows;
vector<int> rowCounts;
for (auto& p : rowCount) {
rows.push_back(p.first);
rowCounts.push_back(p.second);
}
vector<vector<int>> cols;
vector<int> colCounts;
for (auto& p : colCount) {
cols.push_back(p.first);
colCounts.push_back(p.second);
}
// Check if rows are complementary
for (int i = 0; i < n; i++) {
if (rows[0][i] == rows[1][i]) return -1;
}
// Check if cols are complementary
for (int i = 0; i < n; i++) {
if (cols[0][i] == cols[1][i]) return -1;
}
// Check count constraints
if (abs(rowCounts[0] - rowCounts[1]) > 1) return -1;
if (abs(colCounts[0] - colCounts[1]) > 1) return -1;
// Calculate minimum moves for rows
int rowMoves = calculateMoves(rows, rowCounts, n);
// Calculate minimum moves for cols
int colMoves = calculateMoves(cols, colCounts, n);
return rowMoves + colMoves;
}
private:
int calculateMoves(vector<vector<int>>& patterns, vector<int>& counts, int n) {
vector<int> target(n);
if (n % 2 == 1) {
// For odd n, the pattern with more occurrences should start with the more frequent element
vector<int> pattern = (counts[0] > counts[1]) ? patterns[0] : patterns[1];
for (int i = 0; i < n; i++) {
target[i] = pattern[0] ^ (i % 2);
}
} else {
// For even n, try both possibilities and take minimum
vector<int> target1(n), target2(n);
for (int i = 0; i < n; i++) {
target1[i] = patterns[0][0] ^ (i % 2);
target2[i] = patterns[1][0] ^ (i % 2);
}
int moves1 = getMismatchCount(patterns, counts, target1) / 2;
int moves2 = getMismatchCount(patterns, counts, target2) / 2;
return min(moves1, moves2);
}
return getMismatchCount(patterns, counts, target) / 2;
}
int getMismatchCount(vector<vector<int>>& patterns, vector<int>& counts, vector<int>& target) {
int mismatches = 0;
for (int i = 0; i < patterns.size(); i++) {
for (int j = 0; j < target.size(); j++) {
if (patterns[i][j] != target[j]) {
mismatches += counts[i];
break;
}
}
}
return mismatches;
}
};
class Solution:
def movesToChessboard(self, board: List[List[int]]) -> int:
n = len(board)
# Count different row and column patterns
row_patterns = {}
col_patterns = {}
for i in range(n):
row_tuple = tuple(board[i])
row_patterns[row_tuple] = row_patterns.get(row_tuple, 0) + 1
col_tuple = tuple(board[j][i] for j in range(n))
col_patterns[col_tuple] = col_patterns.get(col_tuple, 0) + 1
# Must have exactly 2 patterns for both rows and columns
if len(row_patterns) != 2 or len(col_patterns) != 2:
return -1
# Get patterns and their counts
rows = list(row_patterns.keys())
row_counts = list(row_patterns.values())
cols = list(col_patterns.keys())
col_counts = list(col_patterns.values())
# Check if patterns are complementary
for i in range(n):
if rows[0][i] == rows[1][i] or cols[0][i] == cols[1][i]:
return -1
# Check count constraints
if abs(row_counts[0] - row_counts[1]) > 1 or abs(col_counts[0] - col_counts[1]) > 1:
return -1
def calculate_moves(patterns, counts):
if n % 2 == 1:
# For odd n, determine the target pattern
start_bit = patterns[0][0] if counts[0] > counts[1] else patterns[1][0]
target = tuple(start_bit ^ (i % 2) for i in range(n))
else:
# For even n, try both possibilities
target1 = tuple(patterns[0][0] ^ (i % 2) for i in range(n))
target2 = tuple(patterns[1][0] ^ (i % 2) for i in range(n))
moves1 = sum(counts[i] for i, pattern in enumerate(patterns) if pattern != target1) // 2
moves2 = sum(counts[i] for i, pattern in enumerate(patterns) if pattern != target2) // 2
return min(moves1, moves2)
return sum(counts[i] for i, pattern in enumerate(patterns) if pattern != target) // 2
row_moves = calculate_moves(rows, row_counts)
col_moves = calculate_moves(cols, col_counts)
return row_moves + col_moves
public class Solution {
public int MovesToChessboard(int[][] board) {
int n = board.Length;
// Count row and column patterns
var rowPatterns = new Dictionary<string, int>();
var colPatterns = new Dictionary<string, int>();
for (int i = 0; i < n; i++) {
string rowKey = string.Join("", board[i]);
rowPatterns[rowKey] = rowPatterns.GetValueOrDefault(rowKey, 0) + 1;
string colKey = "";
for (int j = 0; j < n; j++) {
colKey += board[j][i].ToString();
}
colPatterns[colKey] = colPatterns.GetValueOrDefault(colKey, 0) + 1;
}
if (rowPatterns.Count != 2 || colPatterns.Count != 2) {
return -1;
}
var rows = new List<string>(rowPatterns.Keys);
var rowCounts = new List<int> { rowPatterns[rows[0]], rowPatterns[rows[1]] };
var cols = new List<string>(colPatterns.Keys);
var colCounts = new List<int> { colPatterns[cols[0]], colPatterns[cols[1]] };
// Check if patterns are complementary
for (int i = 0; i < n; i++) {
if (rows[0][i] == rows[1][i] || cols[0][i] == cols[1][i]) {
return -1;
}
}
// Check count constraints
if (Math.Abs(rowCounts[0] - rowCounts[1]) > 1 || Math.Abs(colCounts[0] - colCounts[1]) > 1) {
return -1;
}
int CalculateMoves(List<string> patterns, List<int> counts) {
if (n % 2 == 1) {
int startBit = counts[0] > counts[1] ? patterns[0][0] - '0' : patterns[1][0] - '0';
string target = "";
for (int i = 0; i < n; i++) {
target += ((startBit ^ (i % 2)) + '0');
}
int moves = 0;
for (int i = 0; i < patterns.Count; i++) {
if (patterns[i] != target) {
moves += counts[i];
}
}
return moves / 2;
} else {
string target1 = "", target2 = "";
for (int i = 0; i < n; i++) {
target1 += (((patterns[0][0] - '0') ^ (i % 2)) + '0');
target2 += (((patterns[1][0] - '0') ^ (i % 2)) + '0');
}
int moves1 = 0, moves2 = 0;
for (int i = 0; i < patterns.Count; i++) {
if (patterns[i] != target1) moves1 += counts[i];
if (patterns[i] != target2) moves2 += counts[i];
}
return Math.Min(moves1, moves2) / 2;
}
}
int rowMoves = CalculateMoves(rows, rowCounts);
int colMoves = CalculateMoves(cols, colCounts);
return rowMoves + colMoves;
}
}
var movesToChessboard = function(board) {
const n = board.length;
// Count row and column patterns
const rowPatterns = new Map();
const colPatterns = new Map();
for (let i = 0; i < n; i++) {
const rowKey = board[i].join('');
rowPatterns.set(rowKey, (rowPatterns.get(rowKey) || 0) + 1);
const colKey = board.map(row => row[i]).join('');
colPatterns.set(colKey, (colPatterns.get(colKey) || 0) + 1);
}
if (rowPatterns.size !== 2 || colPatterns.size !== 2) {
return -1;
}
const rows = Array.from(rowPatterns.keys());
const rowCounts = rows.map(row => rowPatterns.get(row));
const cols = Array.from(colPatterns.keys());
const colCounts = cols.map(col => colPatterns.get(col));
// Check if patterns are complementary
for (let i = 0; i < n; i++) {
if (rows[0][i]
复杂度分析
| 指标 | 复杂度 |
|---|---|
| 时间 | - |
| 空间 | - |