链接:https://leetcode.cn/problems/transform-to-chessboard/solution/by-xun-ge-v-k6in/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
题目![](https://i-blog.csdnimg.cn/blog_migrate/a9b5cab635ba3073bb6e66dd4b7a4b61.png)
示例![](https://i-blog.csdnimg.cn/blog_migrate/4015d6801b0583aed952596c21d72e4f.png)
思路
解题思路
数学
根据题意,要想变为棋盘,我们首先要检查矩阵的合法性,即是否能变为棋盘,然后再计算变为棋盘移动的次数。
- 检查棋盘的合法性(由于移动行不会改变列,移动列不会改变行,因此我们只需要分别计算)
- 要想变为棋盘,首先每行的0和1的数量要么相等(010101),要么差值为1(01010或者10101)
- 假设第一行是010101,第二行必须为101010,第三行为010101,即第一行和第二行每个位置的数字相反,第三行和第一行相同,以此类推,第i行要么和第一行相同,要么和第一行相反。我们记录相同的行数数量为sameCnt,相反为oppositeCnt,则sameCnt == oppositeCnt或者Math.abs(sameCnt - oppositeCnt) == 1
- 相同的我们计算列的合法性
- 计算移动的次数(通过合法性校验我们计算移动的次数)
- 首先,我们统计错位的个数,每次移动是交换两个错位元素,因此错位的个数一定是偶数个,我们只需要计算第一行和第一列的移动次数即可
- 我们假设第一行是10101010.....,然后计算错位的元素,设错位元素为errorCnt,行元素长度为n
- 若n是偶数,则我们返回变为010101...和101010...的最小交换次数,
- 101010...这种的交换次数为errorCnt / 2
- 010101...这种的交换次数为(n - errorCnt) / 2
- 若n是奇数,则根据0和1的数量大小,只有一种选择,由于错位数量必须为偶数,所以变换为010101...和101010...的错位数量一定是一奇一偶
- 若errorCnt为奇数,返回(n - errorCnt) / 2
- 若errorCnt为偶数,返回errorCnt / 2
- 若n是偶数,则我们返回变为010101...和101010...的最小交换次数,
- 相同的我们计算移动列的次数
代码
#define MIN(a, b) ((a) < (b) ? (a) : (b))
static int countBit(int x) {
int ans = 0;
while (x != 0) {
x &= (x - 1);
ans++;
}
return ans;
}
static int getMoves(int mask, int count, int n) {
int ones = countBit(mask);
if (n & 1) {
/* 如果 n 为奇数,则每一行中 1 与 0 的数目相差为 1,且满足相邻行交替 */
if (abs(n - 2 * ones) != 1 || abs(n - 2 * count) != 1 ) {
return -1;
}
if (ones == (n >> 1)) {
/* 偶数位变为 1 的最小交换次数 */
return n / 2 - countBit(mask & 0xAAAAAAAA);
} else {
/* 奇数位变为 1 的最小交换次数 */
return (n + 1) / 2 - countBit(mask & 0x55555555);
}
} else {
/* 如果 n 为偶数,则每一行中 1 与 0 的数目相等,且满足相邻行交替 */
if (ones != (n >> 1) || count != (n >> 1)) {
return -1;
}
/* 偶数位变为 1 的最小交换次数 */
int count0 = n / 2 - countBit(mask & 0xAAAAAAAA);
/* 奇数位变为 1 的最小交换次数 */
int count1 = n / 2 - countBit(mask & 0x55555555);
return MIN(count0, count1);
}
}
int movesToChessboard(int** board, int boardSize, int* boardColSize){
int rowMask = 0, colMask = 0;
/* 检查棋盘的第一行与第一列 */
for (int i = 0; i < boardSize; i++) {
rowMask |= (board[0][i] << i);
colMask |= (board[i][0] << i);
}
int reverseRowMask = ((1 << boardSize) - 1) ^ rowMask;
int reverseColMask = ((1 << boardSize) - 1) ^ colMask;
int rowCnt = 0, colCnt = 0;
for (int i = 0; i < boardSize; i++) {
int currRowMask = 0;
int currColMask = 0;
for (int j = 0; j < boardSize; j++) {
currRowMask |= (board[i][j] << j);
currColMask |= (board[j][i] << j);
}
/* 检测每一行的状态是否合法 */
if (currRowMask != rowMask && currRowMask != reverseRowMask) {
return -1;
} else if (currRowMask == rowMask) {
/* 记录与第一行相同的行数 */
rowCnt++;
}
/* 检测每一列的状态是否合法 */
if (currColMask != colMask && currColMask != reverseColMask) {
return -1;
} else if (currColMask == colMask) {
/* 记录与第一列相同的列数 */
colCnt++;
}
}
int rowMoves = getMoves(rowMask, rowCnt, boardSize);
int colMoves = getMoves(colMask, colCnt, boardSize);
return (rowMoves == -1 || colMoves == -1) ? -1 : (rowMoves + colMoves);
}
作者:xun-ge-v
链接:https://leetcode.cn/problems/transform-to-chessboard/solution/by-xun-ge-v-k6in/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。