LeetCode 2033. 获取单值网格的最小操作数

一、题目

1、题目描述

  给你一个大小为 m x n的二维整数网格 grid和一个整数 x 。每一次操作,你可以对 grid中的任一元素 加 x或 减 x。单值网格 是全部元素都相等的网格。返回使网格化为单值网格所需的 最小 操作数。如果不能,返回 -1

  样例输入: grid = [[2,4],[6,8]], x = 2
  样例输出: 4

2、基础框架

  • C语言 版本给出的基础框架代码如下:
int minOperations(int** grid, int gridSize, int* gridColSize, int x){
}

3、原题链接

LeetCode 2033. 获取单值网格的最小操作数

二、解题报告

1、思路分析

   ( 1 ) (1) (1) 把所有的数字都先塞到一个数组中,然后对数字进行递增排序。于是问题转变成了求一个 target,所有数和 target差除上 x的和最小。
   ( 2 ) (2) (2) 所有数和 target差必须是x的倍数,所以所有数必须和targetx同余。如果一旦出现不同余,返回-1
   ( 3 ) (3) (3) 由于target的值在1到10000之间,所以可以预先筛选出来所有和每个数同余的target的值。
   ( 4 ) (4) (4) 通过样例可得,target满足极值性质,也就是随着target的从小到大,返回的答案 会先变小,再变大。所以可以利用三分枚举target判断可行性。
   ( 5 ) (5) (5) 对于一个有极值的函数 y = f ( x ) y = f(x) y=f(x) x x x 的区间范围是 [ l , r ] [l, r] [l,r],那么对于 l m i d = 2 l + r 3 lmid = \frac {2l+r}{3} lmid=32l+r r m i d = l + 2 r 3 rmid = \frac {l+2r}{3} rmid=3l+2r
    如果 f ( l m i d ) ≤ f ( r m i d ) f(lmid) \le f(rmid) f(lmid)f(rmid),说明极值不可能在 [ l , l m i d ] [l, lmid] [l,lmid] 之间,所以 r = r m i d − 1 r = rmid - 1 r=rmid1
    如果 f ( l m i d ) > f ( r m i d ) f(lmid) > f(rmid) f(lmid)>f(rmid),说明极值不可能在 [ r m i d , r ] [rmid, r] [rmid,r] 之间,所以 l = l m i d + 1 l = lmid + 1 l=lmid+1
    这就是三分,三分的过程中,不断更新答案,最后返回那个答案。

2、时间复杂度

   最坏时间复杂度 O ( n l o g 3 n ) O(nlog_3n) O(nlog3n)

3、代码详解

int cmp(const void *a, const void *b) {
    return *(int *)a - *(int *)b;
}

int getSum(int *ans, int ansSize, int target, int x) {
    int i;
    int ret = 0;
    for(i = 0; i < ansSize; ++i) {
        ret += abs( target - ans[i] ) / x;
    }
    return ret;
}

int min(int a, int b) {
    if(a == -1) return b;
    if(b == -1) return a;
    return a < b ? a : b;
}

int minOperations(int** grid, int gridSize, int* gridColSize, int x){
    int m = gridSize;
    int n = gridColSize[0];
    int size = m * n;
    int i, j;
    int can[10010], canSize = 0;
    int l, r, mid;
    int *ans = (int *)malloc( sizeof(int) * size );
    size = 0;
    for(i = 0; i < m; ++i) {
        for(j = 0; j < n; ++j) {
            ans[size++] = grid[i][j];
        }
    }
    qsort(ans, size, sizeof(int), cmp);
    for(i = 1; i < size; ++i) {
        if(ans[i]%x != ans[i-1]%x) {
            return -1;
        }
    }
    //  + + + + + + + - - - - - - - - - 
    //  + + + + - - - - - - - - - - - -
    //  + + - - - - - - - - - - - - - - 
    for(i = 0; i <= 10000; ++i) {
        if(i % x == ans[0] % x) {
            can[canSize++] = i;
        }
    }

    l = 0;
    r = canSize - 1;
    int ret = -1;
    while(l <= r) {
        int lmid = (2*l + r) / 3;
        int rmid = (l + r*2) / 3;
        int lsum = getSum(ans, size, can[lmid], x);
        int rsum = getSum(ans, size, can[rmid], x);

        if(lsum < rsum) {
            r = rmid - 1;
        }else {
            l = lmid + 1;
        }
        ret = min(ret, lsum);
        ret = min(ret, rsum);
    }
    return ret;

}

/*
1、先给抽象到一个数组中
2、排序
3、a b c d e f g

假设 所有数变成 target 能够使得步数最小

a + x * a0 = target
b + x * b0 = target
...
g - x * g0 = target


1 2 3 4 5

1  -> 0 + 1 + 2 + 3 + 4    = 10
2  -> 1 + 0 + 1 + 2 + 3    = 7
3  -> 2 + 1 + 0 + 1 + 2    = 6
4  -> 3 + 2 + 1 + 0 + 1    = 7
5  -> 4 + 3 + 2 + 1 + 0    = 10






*/

三、本题小知识

  一定还有更加简单的方案。


四、加群须知

  相信看我文章的大多数都是「 大学生 」,能上大学的都是「 精英 」,那么我们自然要「 精益求精 」,如果你还是「 大一 」,那么太好了,你拥有大把时间,当然你可以选择「 刷剧 」,然而,「 学好算法 」,三年后的你自然「 不能同日而语 」
  那么这里,我整理了「 几十个基础算法 」 的分类,点击开启:

🌌《算法入门指引》🌌

  如果链接被屏蔽,或者有权限问题,可以私聊作者解决。

  大致题集一览:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述



在这里插入图片描述


  为了让这件事情变得有趣,以及「 照顾初学者 」,目前题目只开放最简单的算法 「 枚举系列 」 (包括:线性枚举、双指针、前缀和、二分枚举、三分枚举),当有 一半成员刷完 「 枚举系列 」 的所有题以后,会开放下个章节,等这套题全部刷完,你还在群里,那么你就会成为「 夜深人静写算法 」专家团 的一员。
  不要小看这个专家团,三年之后,你将会是别人 望尘莫及 的存在。如果要加入,可以联系我,考虑到大家都是学生, 没有「 主要经济来源 」,在你成为神的路上,「 不会索取任何 」
  🔥联系作者,或者扫作者主页二维码加群,加入刷题行列吧🔥


🔥让天下没有难学的算法🔥

C语言免费动漫教程,和我一起打卡!
🌞《光天化日学C语言》🌞

让你养成九天持续刷题的习惯
🔥《九日集训》🔥

入门级C语言真题汇总
🧡《C语言入门100例》🧡

组团学习,抱团生长
🌌《算法零基础100讲》🌌

几张动图学会一种数据结构
🌳《画解数据结构》🌳

竞赛选手金典图文教程
💜《夜深人静写算法》💜
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

英雄哪里出来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值