携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情
782. 变为棋盘
一个 的二维网络 仅由 和 组成 。
每次移动,你能任意交换两列或是两行的位置。
返回 将这个矩阵变为 “棋盘” 所需的最小移动次数 。如果不存在可行的变换,输出 -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时也是合法的棋盘,也是合法的棋盘.
数据范围
n == board.lengthn == board[i].length2 <= n <= 30board[i][j]将只包含0或1
思路
要使得任意一格的上下左右四个方向的值均与本身不同的矩阵。 则对于自上而下每一个格子,它的下方和右方一定和自己不一样。
则最后的情况一定对应着这种情况:
1 0 1 0 1 0 1 0 ....
0 1 0 1 0 1 0 1 ....
1 0 1 0 1 0 1 0 ....
(从0 或 1 行开始)
那么,仔细观察一下,相邻两行想与一定为0,且只会出现两种类型的行
存在任意次交换两列之后,并不会改变行与行之间的关系
那么,抛开行不谈,我们的列是不是一样呢?
显然,也是一样的。所以我们可以先 “还原” 行的排列
再“还原”列的排列
下一个问题: 对于一行只包含a和b的序列 如何排列才能达到排列次数最少?
这个时候把奇数和偶数分开考虑
若是偶数,那么ababab或者bababa都是一样的,所以,取最小的即可
若是奇数,那么这个时候出现多1个的数一定排在偶数位置(从0开始)
分类讨论即可
代码
class Solution {
public:
int movesToChessboard(vector<vector<int>>& board) {
vector<int> ve(board.size());
map<int, int> mp;
for (int i = 0; i < board.size(); i ++) {
int res = 0;
for (auto v : board[i])
res = (res << 1) + v;
mp[res] ++;
ve[i] = res;
if (mp.size() > 2) return -1;
}
auto t = mp.begin();
auto a = *t;
auto b = *(++ t);
if (abs(a.second - b.second) > 1 || (a.first & b.first)) return -1;
int ans = 0, odd = 0, even = 0, n = board.size();
int f = 0;
if (a.second < b.second) f = 1;
for (int i = 0; i < n; i ++)
if (ve[i] == a.first && i % 2 != f) odd ++;
if (n % 2 == 0) ans = min(a.second - odd, odd);
else ans = odd;
int x = a.first, cnt = 0;
int len = board.back().size();
odd = 0, even = 0;
for (int i = 0, j = len -1; i < len; j --, i ++) {
if (x >> j & 1) {
cnt ++;
if (i % 2) odd ++;
else even ++;
}
}
if (len % 2 == 0) ans += min(odd, cnt - odd);
else {
if (cnt > len - cnt) ans += odd;
else ans += even;
}
if (abs(cnt - (len - cnt)) > 1) return -1;
return ans;
}
};