782 变为棋盘——Leetcode天天刷(2022.8.23)【数学:降维计算】

文章目录

  • 782 变为棋盘——Leetcode天天刷(2022.8.23)【数学:降维计算】
    • 前言
    • 题目描述
      • 示例
      • 提示信息
    • 本地调试运行
      • 输入格式
      • 输出格式
      • 输入样例
      • 输出样例
    • 解法——数学,降维计算,模拟
      • 判断是否有解
      • 计算移动次数
      • Code(C++)
      • 算法分析+解题效率
    • 后话

前言

打了下今天的每日一题,不得不说,不愧是 Hard,虽然感觉自己经过集训的洗礼,能直接手撕了。But,想了几分钟,确实没什么好的思路,但是,可以知道纯暴力的棋盘搜索是不行的。

在看了 官方题解三叶小姐姐 的文字思路后,才知道这题目可以降维来求解!理解了思路后,即使不看代码,也可以凭借对思路的理解来手撸代码实现了。

题目描述

题目传送门

感兴趣的可以点击跳转打题。

简述一下:就是一个 n ∗ n n * n n∗n 的二维数组,每个单元格不是 0 0 0 就是 1 1 1。

我们可以 任意 交换 或者两 ,最终使这个矩阵变为 棋盘,棋盘就是任意一个格子的上下左右四个方向的值和当前值不同的矩阵。

我们需要返回输出最少需要的交换次数,如果不存在可行的交换,则输出 − 1 -1 −1。

示例

输入: board = [[0,1,1,0],[0,1,1,0],[1,0,0,1],[1,0,0,1]]
输出: 2
解释:一种可行的变换方式如下,从左到右:
第一次移动交换了第一列和第二列。
第二次移动交换了第二行和第三行。输入: board = [[0, 1], [1, 0]]
输出: 0
解释: 注意左上角的格值为0时也是合法的棋盘,也是合法的棋盘.输入: board = [[1, 0], [1, 0]]
输出: -1
解释: 任意的变换都不能使这个输入变为合法的棋盘。来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/transform-to-chessboard
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处

提示信息

本地调试运行

这个就还好,不想树之类的题目,需要写额外的代码来构造树,我们只需要做好输入格式即可。

输入格式

假定多组输入,每组第一行输入一个整数n,然后接下来n行,每行输入n个整数。

输出格式

根据题目的类函数结构,我们直接输出结果即可。

输入样例

4
0 1 1 0
0 1 1 0
1 0 0 1
1 0 0 12
0 1
1 02
1 0
1 0

输出样例

2
0
-1

像leetcode这种OJ比较人性化,如果没通过会返回不能通过的测试数据,如果是普遍的一些OJ,我们其实可以在本地写多一个数据生成器,加一个暴力算法,来确保我们代码能够正确。

解法——数学,降维计算,模拟

参考自官方题解和三叶小姐姐的题解文字思路!

我们首先需要明白一个本质问题:行列变化的独立性,以 **行交换 **为例,我们任意交换两行,我们会发现列的种类没有发生变化,只是0,1的分布改变了。从这个特性,我们就可以知道,行,列交换的顺序先后,对结果不会有影响!这也是我们可以做降维计算的基础!

降维计算,其实就是分维度来计算,我们原本是需要考虑通过行列变化来使得整个矩阵变为棋盘,这时我们可以不用考虑整个棋盘,而是考虑其中的一行一列!

首先我们考虑 有解 的情况:如果有解,那么对于第一行,如果n为偶数,那么0和1的数量一定相同;如果是奇数,那么差值为1,这个对每一行,每一列都成立。而对于其他行,和第一行的对称位置,要么就完全相同,要么就是完全相反,且相同的行数,和不同的行数,和0,1同理,差值不会超过1。

所以,我们只需要将第一行变为0101。。。或者1010。。。这样的间隔形式即可。

列同理。

那么这个变为棋盘的问题,就拆分成了两个部分:先判断是否有解,在判断需要变换的次数。

判断是否有解

只需要满足我们前面提到的条件即可,我们再梳理下:

  1. 第一行、列中1,0的数目差的绝对值,设为 sub,有如下公式
    s u b = { 0 , n % 2 = = 0 1 , n % 2 = = 1 sub = \left\{ \begin{aligned} 0,\qquad n \% 2 == 0 \\ 1,\qquad n \% 2 == 1 \end{aligned} \right. sub={0,n%2==01,n%2==1​

  2. 其他行,和第一行,要么完全相同,要么完全相反,列同理

  3. 接条件2,相同和相反的行数以及列数,差值的绝对值和条件1的公式相同。

计算移动次数

我们只需判断第一行和第一列即可。

具体如下, 记移动次数为times

因为下标是从0开始,我们可以取偶数位下标的数字,我们分别计算第一行、列的偶数位下标对应的数字加和,记为 r1c1

  1. n为偶数,那么无外乎是把偶数位不是1的数字换成1,或者把数字1换成0,有如下公式
    t i m e s + = min ⁡ ( r 1 , n / 2 − r 1 ) times += \min(r1, n/2-r1) times+=min(r1,n/2−r1)
    列同理。

  2. n为奇数,我们则需要根据0,1谁的数量多,决定将多的放偶数位。

思路清晰后,就上代码!

Code(C++)

#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>using namespace std;class Solution
{public:// 分维度计算// 分两步走,第一步判断是否有解——棋盘是否正确!第二步计算移动的次数// 有解需要判断的条件:1.第一行、列中1,0的数目差值绝对值=1(n为奇数)或者=0(偶数)// 2.其他行和列中不是第一行/列的相同,就是相反,并整个棋盘中的相同和相反状态的数量差值<=1// 计算移动次数,由于行、列移动的独立性,我们只需要判断第一行、第一列的情况即可// 根据n的奇偶性,和0,1的数量,在判断奇偶位的0/1的数目,即为移动次数int movesToChessboard(vector<vector<int>>& board){int n = board.size();int rowMask = 0, ReverRowMask = 0;        // 第一行的状态// 第一行中0和1的数量int count0 = 0, count1 = 0;// 遍历第一行,计算状态掩码和反状态掩码,并记录0,1的个数for (int i = 0; i < n; ++i){rowMask <<= 1;ReverRowMask <<= 1;if (board[0][i]){rowMask |= 1;++count1;}else{ReverRowMask |= 1;++count0;}}// 判断第一行中0,1个数是否合理if (!((n % 2 == 1 && abs(count0 - count1) == 1) || (n % 2 == 0 && count0 == count1))){return -1;}int couRM = 1, couRRM = 0;     // 和第一行状态相同的行数,相反的行数// 接下来遍历每一行for (int i = 1; i < n; ++i){int rm = 0;// 计算该行的状态for (int j = 0; j < n; ++j){rm <<= 1;rm |= board[i][j];}// 和第一行的状态以及反转态比较并计数if (rm == rowMask){++couRM;}else if (rm == ReverRowMask){++couRRM;}// 如果两个都不同,那就无解else{return -1;}}// 接下来根据n的奇偶性,和rouMask相同和相反的行数,来判断是否有解if (!((n % 2 == 1 && abs(couRM - couRRM) == 1) || (n % 2 == 0 && couRM == couRRM))){return -1;}// 行判断完了,接下来就是列的判断,基本同上int colMask = 0, rColMask = 0;int count2 = 0, count3 = 0;for (int i = 0; i < n; ++i){colMask <<= 1;rColMask <<= 1;if (board[i][0]){colMask |= 1;++count2;}else{rColMask |= 1;++count3;}}if (!((n % 2 == 1 && abs(count2 - count3) == 1) || (n % 2 == 0 && count2 == count3))){return -1;}int couCM = 1, couRCM = 0;for (int i = 1; i < n; ++i){int cm = 0;for (int j = 0; j < n; ++j){cm <<= 1;cm |= board[j][i];}if (colMask == cm){++couCM;}else if (cm == rColMask){++couRCM;}else{return -1;}}if (!((n % 2 == 1 && abs(couCM - couRCM) == 1) || (n % 2 == 0 && couCM == couRCM))){return -1;}// 接下来是计算需要移动的次数int ans = 0;int r1 = 0, c1 = 0;      // 计算第一行、第一列中,下标从0开始的偶数位1的个数for (int i = 0; i < n; i += 2){r1 += board[0][i];c1 += board[i][0];}// n为偶数,偶数下标用0,用1都行if (n % 2 == 0){// 有两种策略,一种是偶数下标中的0换为1,一种是将1换为0,取最小值ans += min(count1 - r1, r1) + min(count2 - c1, c1);}// 第一个必须是数量较多的else{// 第一行0比1多if (count0 > count1){ans += r1;       // 用0换1}else{ans += count1 - r1;      // 用1换0}if (count3 > count2){ans += c1;}else{ans += count2 - c1;}}return ans;}
};int main()
{int n;Solution* sol = new Solution();while (~scanf("%d", &n)){vector<vector<int>> board(n, vector<int>(n));for (int i = 0; i < n; ++i){for (int j = 0; j < n; ++j){scanf("%d", &board[i][j]);}}printf("%d\n", sol->movesToChessboard(board));}delete sol;return 0;
}

算法分析+解题效率

时间复杂度 O ( n 2 ) O(n^2) O(n2),遍历两次矩阵。

空间复杂度 O ( 1 ) O(1) O(1),只使用了几个变量。

后话

不愧是Hard,打的有点慢!

782 变为棋盘——Leetcode天天刷(2022.8.23)【数学:降维计算】相关推荐

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

    链接:https://leetcode.cn/problems/transform-to-chessboard/solution/by-xun-ge-v-k6in/ 来源:力扣(LeetCode) 著 ...

  2. 图解LeetCode——782. 变为棋盘(难度:困难)

    一.题目 一个 n * n 的二维网络 board 仅由 0 和 1 组成 .每次移动,你能任意交换两列或是两行的位置. 返回 将这个矩阵变为  "棋盘"  所需的最小移动次数 . ...

  3. leetcode 782. Transform to Chessboard | 782. 变为棋盘(Java)

    题目 https://leetcode.com/problems/transform-to-chessboard/ 题解 没思路,看了答案.这道题没有套路,需要发现独特的规律,参考: 简单易懂的解法 ...

  4. 看了这篇 LeetCode 的刷题心得,再也不用抄别人代码了

    作者:VioletJack 原文:<LeetCode 算法题刷题心得>https://www.jianshu.com/p/8876704ea9c8 花了十几天,把<算法>看了一 ...

  5. leetcode分类刷题笔记

    leetcode分类刷题笔记--基于python3 写在前面 1.做题如果实在想不出时间复杂度比较优的解法,可以先写出暴力解法,尝试在其基础上优化 2.排序.双指针.二分等--经常可以优化时间复杂度 ...

  6. 【Leetcode】 刷题之路1(python)

    leetcode 刷题之路1(python) 看到有大佬总结了一些相关题目,想着先刷一类. 1.两数之和 15.三数之和 16.最接近的三数之和 11.盛最多的水 18.四数之和 454.四数相加II ...

  7. leetcode每日刷题计划-简单篇day8

    leetcode每日刷题计划-简单篇day8 今天是纠结要不要新买手机的一天QAQ想了想还是算了吧,等自己赚钱买,加油 Num 70 爬楼梯 Climbing Stairs class Solutio ...

  8. LeetCode代码刷题(17~24)

    目录 17. 电话号码的字母组合 18. 四数之和 19. 删除链表的倒数第 N 个结点 20. 有效的括号 21. 合并两个有序链表 22. 括号生成 23. 合并K个升序链表 24. 两两交换链表 ...

  9. Leetcode-第782题-困难-《变为棋盘》

    题目 题目链接 一个 n × n n\times n n×n的二维网络 b o a r d board board仅由 0 0 0和 1 1 1组成 .每次移动,你能任意交换两列或是两行的位置. 返回 ...

最新文章

  1. CMakeFile命令之file
  2. Mysql Mariadb 创建新用户
  3. Qt designer设计界面
  4. 汇编:call指令的应用
  5. Java08-day08【API(概述、使用)、String-StringBuilder(概述、构造方法、特点、方法)、StringBuilder和String相互转化】
  6. 平面设计师常用的网站|素材路上
  7. 事务注解放到类上面 下面私有方法有效吗_【面试】足够应付面试的Spring事务源码阅读梳理(建议珍藏)...
  8. 性能测试-ApacheBench
  9. Gazebo Ros入门
  10. 猫扑_猫女郎图片批量下载器
  11. 行政区划代码2020(SQL)--(上)
  12. 升级yosemite后java出错的解决
  13. 统一社会信用代码校验规则
  14. python的if __name__ == “__main__“语法错误SyntaxError: invalid syntax
  15. 光照度和光强度的区别
  16. 备忘录吕吕没有备忘录十新建_备忘录字段焦虑
  17. ACM--过沼泽--模拟--HDOJ 5477--A Sweet Journey
  18. 网管员必知:常用电脑密码破解
  19. “积微者速成”与敏捷实践
  20. STM32:DMA方式接收SPI总线数据,并按照协议进行处理

热门文章

  1. java的actionlistener_「actionlistener」Java——事件处理机制监听者基础(一)动作监听ActionListener - seo实验室...
  2. Facebook的Libra “区块链”到底是如何运作的?
  3. RGB-D Camera深度相机主流方案对比
  4. 数据库课程设计个人总结报告
  5. POJ 3131 Cubic Eight-Puzzle
  6. 《如何写好科研论文》《学术规范与论文写作》-长江雨课堂-2022秋季期末考试答案分享
  7. 面向对象、封装、就近原则及this关键字
  8. 【译】Chrome 扩展 : 扩展是什么?
  9. 网易闪电邮明文密码还原
  10. Excel如何将数据复制到合并单元格中