LeetCode·每日一题·782.变为棋盘·数学

链接:https://leetcode.cn/problems/transform-to-chessboard/solution/by-xun-ge-v-k6in/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 

题目

示例

 

思路

解题思路
数学
根据题意,要想变为棋盘,我们首先要检查矩阵的合法性,即是否能变为棋盘,然后再计算变为棋盘移动的次数。

  • 检查棋盘的合法性(由于移动行不会改变列,移动列不会改变行,因此我们只需要分别计算)
    • 要想变为棋盘,首先每行的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
    • 相同的我们计算移动列的次数

代码

#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)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值