[LeetCode] 每日一题 782. 变为棋盘
题目链接
题目描述
一个 n x n
的二维网络 board
仅由 0
和 1
组成 。每次移动,你能交换任意两列或是两行的位置。
返回 将这个矩阵变为 “棋盘” 所需的最小移动次数 。如果不存在可行的变换,输出 -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
题解
解题思路
为了解决这个问题,我们可以分以下几步进行:
1. 合法性检查
首先,一个合法的棋盘必须满足:
第一行的 0 和 1 的个数之差不能超过 1。
第一列的 0 和 1 的个数之差不能超过 1。
每一行(或列)与第一行(或列)的关系必须是完全相同或完全相反。
如果不满足上述任意一个条件,直接返回 -1。
2. 计算最小交换次数
为了最小化交换次数,需要分别考虑以下两种情况:
调整行顺序:
假设第一行满足棋盘的排列(如
0101...
或1010...
),统计与第一行不匹配的行数(错位行),通过交换行位置调整为正确排列。对于奇偶长度的矩阵,错位的行数可能有两种排列,我们取较小的交换次数。
调整列顺序:
同理,假设第一列满足棋盘的排列,统计与第一列不匹配的列数(错位列),通过交换列位置调整为正确排列。
最终结果是行调整和列调整的交换次数之和。
3. 特殊情况处理
当矩阵的大小 nnn 为奇数时,最终棋盘的排列方式是确定的(
0101...
或1010...
的数量不能相同),只需判断与之对应的交换次数。当 nnn 为偶数时,两种棋盘排列方式的交换次数都可行,取较小值即可。
代码实现
以下是根据上述思路的代码实现:
class Solution {
public int movesToChessboard(int[][] board) {
int n = board.length;
// 检查第一行 0 和 1 的数量是否平衡
int cnt0 = 0;
for (int i = 0; i < n; i++) {
if (board[0][i] == 0) {
cnt0++;
}
}
// 如果差值超过 1,无法转换成棋盘
if (Math.abs(cnt0 - (n - cnt0)) > 1) {
return -1;
}
// 检查第一列 0 和 1 的数量是否平衡
cnt0 = 0;
for (int i = 0; i < n; i++) {
if (board[i][0] == 0) {
cnt0++;
}
}
// 如果差值超过 1,无法转换成棋盘
if (Math.abs(cnt0 - (n - cnt0)) > 1) {
return -1;
}
// 检查每一行是否符合规则(与第一行完全相同或完全不同)
for (int[] row : board) {
boolean same = row[0] == board[0][0];
for (int i = 0; i < n; i++) {
// 如果不符合,无法转换成棋盘
if ((row[i] == board[0][i]) != same) {
return -1;
}
}
}
int res = 0;
// 计算第一行的最小交换次数
int misMatch0 = 0, misMatch1 = 0;
for (int i = 0; i < n; i++) {
// 偶数位
if (i % 2 == 0) {
// 偶数位应该是 0
if (board[0][i] != 0) {
misMatch0++;
}
// 偶数位应该是 1
if (board[0][i] != 1) {
misMatch1++;
}
} // 奇数位
else {
// 奇数位应该是 1
if (board[0][i] != 1) {
misMatch0++;
}
// 奇数位应该是 0
if (board[0][i] != 0) {
misMatch1++;
}
}
}
if (n % 2 == 0) {
res += Math.min(misMatch0, misMatch1) / 2;
} else {
res += (misMatch0 % 2 == 0) ? misMatch0 / 2 : misMatch1 / 2;
}
// 计算第一列的最小交换次数
misMatch0 = 0;
misMatch1 = 0;
for (int i = 0; i < n; i++) {
// 偶数位
if (i % 2 == 0) {
// 偶数位应该是 0
if (board[i][0] != 0) {
misMatch0++;
}
// 偶数位应该是 1
if (board[i][0] != 1) {
misMatch1++;
}
} // 奇数位
else {
// 奇数位应该是 1
if (board[i][0] != 1) {
misMatch0++;
}
// 奇数位应该是 0
if (board[i][0] != 0) {
misMatch1++;
}
}
}
if (n % 2 == 0) {
res += Math.min(misMatch0, misMatch1) / 2;
} else {
res += (misMatch0 % 2 == 0) ? misMatch0 / 2 : misMatch1 / 2;
}
return res;
}
}
总结
通过合法性检查,快速过滤无法变成棋盘的矩阵。
分别计算行和列的最小交换次数,解决错位问题。
考虑矩阵大小为奇偶数时的特殊情况,确保结果正确。
时间复杂度为 O(n^2),适用于大部分情况。
希望这篇分享能为你带来启发!如果你有任何问题或建议,欢迎在评论区留言,与我共同交流探讨。