3240. 最少翻转次数使二进制矩阵回文 II

Powered by:NEFU AB-IN

Link

文章目录

  • [3240. 最少翻转次数使二进制矩阵回文 II](#3240. 最少翻转次数使二进制矩阵回文 II)

3240. 最少翻转次数使二进制矩阵回文 II

题意

给你一个 m x n 的二进制矩阵 grid 。

如果矩阵中一行或者一列从前往后与从后往前读是一样的,那么我们称这一行或者这一列是 回文 的。

你可以将 grid 中任意格子的值 翻转 ,也就是将格子里的值从 0 变成 1 ,或者从 1 变成 0 。

请你返回 最少 翻转次数,使得矩阵中 所有 行和列都是 回文的 ,且矩阵中 1 的数目可以被 4 整除 。

思路

并查集+思维+dp

  1. 如果一个数变的话,为了保持回文,那么行对称和列对称的两个数也得变(其实是四个数),所以这就形成了一个集合,且集合与集合之间互不影响
  2. 即使用并查集维护,并维护集合中1的个数和集合大小
  3. 使用dp状态机,dp[i]表示:1的数量 %4=i 的最小操作次数,当遇到根节点,对集合针对0和1都进行翻转,因为集合只能全为0和1
  4. 拓展性更强,把4的倍数改成 2 3 4 5 6 7 8 9 的倍数都没关系

代码

python 复制代码
'''
Author: NEFU AB-IN
Date: 2024-08-07 10:04:41
FilePath: \LeetCode\3240\3240.py
LastEditTime: 2024-08-07 11:06:07
'''
# 3.8.19 import
import random
from collections import Counter, defaultdict, deque
from datetime import datetime, timedelta
from functools import lru_cache, reduce
from heapq import heapify, heappop, heappush, nlargest, nsmallest
from itertools import combinations, compress, permutations, starmap, tee
from math import ceil, comb, fabs, floor, gcd, hypot, log, perm, sqrt
from string import ascii_lowercase, ascii_uppercase
from sys import exit, setrecursionlimit, stdin
from typing import Any, Callable, Dict, List, Optional, Tuple, TypeVar, Union

# Constants
TYPE = TypeVar('TYPE')
N = int(2e5 + 10)
M = int(20)
INF = int(1e12)
OFFSET = int(100)
MOD = int(1e9 + 7)

# Set recursion limit
setrecursionlimit(int(2e9))


class Arr:
    array = staticmethod(lambda x=0, size=N: [x() if callable(x) else x for _ in range(size)])
    array2d = staticmethod(lambda x=0, rows=N, cols=M: [Arr.array(x, cols) for _ in range(rows)])
    graph = staticmethod(lambda size=N: [[] for _ in range(size)])


class Math:
    max = staticmethod(lambda a, b: a if a > b else b)
    min = staticmethod(lambda a, b: a if a < b else b)


class IO:
    input = staticmethod(lambda: stdin.readline().rstrip("\r\n"))
    read = staticmethod(lambda: map(int, IO.input().split()))
    read_list = staticmethod(lambda: list(IO.read()))


class Std:
    class UnionFind:
        """Union-Find data structure."""

        def __init__(self, n):
            self.n = n
            self.parent = list(range(n))  # Parent pointers
            self.rank = Arr.array(1, n)  # Rank (approximate tree height)
            self.size = Arr.array(1, n)  # Size arrays for each node
            self.cnt_1 = Arr.array(0, n)

        def find(self, p):
            """Find the root of the element p with path compression."""
            if self.parent[p] != p:
                self.parent[p] = self.find(self.parent[p])  # Path compression
            return self.parent[p]

        def union(self, p, q):
            """Union the sets containing p and q using union by rank and merge data if available."""
            rootP = self.find(p)
            rootQ = self.find(q)

            if rootP != rootQ:
                # Union by rank
                if self.rank[rootP] > self.rank[rootQ]:
                    self.parent[rootQ] = rootP
                    self.size[rootP] += self.size[rootQ]
                    self.cnt_1[rootP] += self.cnt_1[rootQ]
                    return rootP
                elif self.rank[rootP] < self.rank[rootQ]:
                    self.parent[rootP] = rootQ
                    self.size[rootQ] += self.size[rootP]
                    self.cnt_1[rootQ] += self.cnt_1[rootP]
                    return rootQ
                else:
                    self.parent[rootQ] = rootP
                    self.size[rootP] += self.size[rootQ]
                    self.cnt_1[rootP] += self.cnt_1[rootQ]
                    self.rank[rootP] += 1
                    return rootP
            return rootP  # They are already in the same set

# --------------------------------------------------------------- Division line ------------------------------------------------------------------


class Solution:
    def minFlips(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])
        uf = Std.UnionFind(m * n)

        def index(i, j): return i * n + j

        for i in range(m):
            for j in range(n):
                uf.cnt_1[index(i, j)] = grid[i][j]

        for i in range(m):
            for j in range(n):
                uf.union(index(i, j), index(i, n - j - 1))
                uf.union(index(i, j), index(m - i - 1, j))

        dp = [0, INF, INF, INF]  # dp[i]表示:1的数量%4=i的最小操作次数
        for i in range(m):
            for j in range(n):
                if uf.find(index(i, j)) == index(i, j):
                    f = [INF] * 4
                    for x in range(4):  # 每个连通块内,要么全1,要么全0
                        # 全变为0,则需要将该连通块的所有1翻转
                        f[(x + 0) % 4] = Math.min(f[(x + 0) % 4], dp[x] + uf.cnt_1[index(i, j)])
                        # 全变为1,则需要将该连通块的所有0翻转,0的数量是总数量减1的数量
                        f[(x + uf.size[index(i, j)]) % 4] = Math.min(f[(x + uf.size[index(i, j)]) % 4], dp[x] + uf.size[index(i, j)] - uf.cnt_1[index(i, j)])
                    dp = f
        return dp[0]
相关推荐
Rock_yzh11 小时前
LeetCode算法刷题——54. 螺旋矩阵
数据结构·c++·学习·算法·leetcode·职场和发展·矩阵
凯子坚持 c1 天前
体系化AI开发方案:豆包新模型矩阵与PromptPilot自动化调优平台深度解析
人工智能·矩阵·自动化
草莓熊Lotso1 天前
《算法闯关指南:优选算法--前缀和》--31.连续数组,32.矩阵区域和
c++·线性代数·算法·矩阵
logocode_li1 天前
面试 LoRA 被问懵?B 矩阵初始化为 0 的原因,大多数人拿目标来回答
人工智能·python·面试·职场和发展·矩阵
AI科技星1 天前
张祥前统一场论:引力场与磁矢势的关联,反引力场生成及拉格朗日点解析(网友问题解答)
开发语言·数据结构·经验分享·线性代数·算法
simon_skywalker2 天前
线性代数及其应用习题答案(中文版)第一章 线性代数中的线性方程组 1.5 线性方程组的解集(1)
线性代数
跨境摸鱼2 天前
AI 赋能!亚马逊竞争情报的“重构式”升级,破解竞品迷局
人工智能·矩阵·重构·跨境电商·亚马逊·防关联
独自破碎E2 天前
矩阵区间更新TLE?试试二维差分
java·线性代数·矩阵
simon_skywalker2 天前
线性代数及其应用习题答案(中文版)第一章 线性代数中的线性方程组 1.6 线性方程组的应用
线性代数
劈星斩月2 天前
3Blue1Brown《线性代数的本质》矩阵乘法与线性变换复合
线性代数·线性变换·矩阵乘法