LeetCode 1502 - 1505

判断能否形成等差数列

给出一个数组,问能不能把这个数组排成一个等差数列一个等差数列必然是单调的,前一项减后一项的值是定值),如果公差是正数,这个序列是单调递增的,如果公差是负数,这个序列是单调递减的,如果公差是 0,就是一个常数数列

先排序排成单调的,排序后看每一个相邻两项之间的差是不是定值就可以了,如果每两项之间的差都是定值的话,就是一个等差数列,否则不是等差数列

数据范围比较小:1 k

class Solution {
public:
    bool canMakeArithmeticProgression(vector<int>& arr) {
        //先排序
        sort(arr.begin(),arr.end());
        //从前往后枚举每两项之间的公差是不是和第一个公差是一样的
        for(int i = 2;i < arr.size();i++ )
            if(arr[i] - arr[i - 1] != arr[1] - arr[0])
                //如果不一样不是等差数列返回false
                return false;
        //是等差数列返回true
        return true;        
    }
};

所有蚂蚁掉下来前的最后一刻

 

有一根长度是 n 的木板, 长度是 n 的木板上一共有 n + 1 个点,也就是从 0 到 n

一个长度是 n 的木板上有两种蚂蚁:

第一种蚂蚁向左走,用红色来表示,当然,某个点上可能会有多个蚂蚁

第二种蚂蚁向右走,用绿色来表示,向左走的蚂蚁每秒走一单位距离,向右走的蚂蚁也是每秒走一单位距离

如果两个蚂蚁相遇的话,相遇的一瞬间两只蚂蚁就会立即回头,回头的时间可以忽略不计:等价于相遇后互换身份,然后继续往前走,这样就比较容易求出每个蚂蚁掉下去的时间了,因为每个蚂蚁不会改变方向,例如下图中位置 4 的蚂蚁掉下去的时间就是 4,因为要走 4 步

如果蚂蚁走出边界就会掉下去,问最后一个蚂蚁掉下去的时间?

如果按照定义来求是非常复杂的,因为中间相遇的次数会非常多

数据范围:1 w,算法的时间复杂度应该控制在 O( n√ n ) 或者 O( nlogn ) 以内

分别判断向左走的蚂蚁,每个蚂蚁要走多长时间才能掉下去,向右走的蚂蚁,每个蚂蚁要走多长时间才能掉下去,取一个最大值即可,时间复杂度为 O( n )

class Solution {
public:
    int getLastMoment(int n, vector<int>& left, vector<int>& right) {
        //存储结果
        int res = 0;
        //先计算左边
        for(auto x : left)  res = max(res,x);
        //再计算右边
        for(auto x : right) res = max(res,n - x);
        return res;
    }
};

统计全 1 子矩形

 

给出一个 0 1 矩阵,让我们求一下这个 0 1 子矩阵里面有多少个子矩阵全是 1,其实是一个计数问题

需要把每一个子矩阵分类,然后每一类分别求,一定要保证不重复,分类的方式有很多种,可以按长宽来分类

先枚举 1 × 1 的,再枚举 1 × 2 的. . .

也可以枚举每一个子矩阵的右下角

位于 [ 2,0 ] 位置上的点有三个子矩阵

位于 [ 2,1 ] 位置上的点有很多种,需要枚举。我们可以按照宽度为区分:宽度是 1 的话有两个子矩阵,宽度是 2 的话也有两个子矩阵

数据范围:150,只要把时间复杂度控制在 O( n^3 ) 就可以过,但是可以通过单调栈优化为 O( n^2 )

暴力做法

枚举顺序,先枚举右下角,当右下角确定之后,再枚举宽度(宽度是 1 的有几个、宽度是 2 的有几个)

看一下如果宽度是 1 怎么求个数?

就是看一下这个 1 往上有多少个连续的 1 就可以了,假设这个 1 往上有连续的 5 个 1,那么以这个格子为右下角,并且宽度是 1 的矩形就会有 5 个(宽度的 1 的矩形的个数就是这个点往上数有多少个连续的 1

枚举完 1 之后,再枚举宽度是 2 的,宽度是 2 怎么求个数?比方说第 2 列一共有 6 个 1,宽度是 2 要保证这两列里都有 1 才可以,它往上取最多能数多少呢?应该是这两列的高度的最小值,取一个 min,min = 5,所以以这个点为右下角,并且宽度是 2 的矩形的个数也是 5 个

依次类推,枚举完 2 之后,再枚举宽度是 3 的,宽度是 3 的矩形个数有多少个?其实是要保证这 3 列往上数都有 1,往上数的个数应该是这 3 列的高度取 min,min = 4,所以以这个点为右下角,并且宽度是 3 的矩形个数有 4 个

宽度从 1 开始枚举,一直往前枚举就可以了

两重循环枚举右下角,再来一重循环枚举宽度,宽度从 1 开始枚举,一直往左扩,对于每一个宽度往上数的高度最大是多少:应该是往左扩的过程当中,每一列的高度取一个最小值,就是往上能够扩的高度,就是矩形的个数

所以往左扩的过程当中应该是边扩边取最小值

需要预处理每一个数往上最多有多少个 1→  可以通过递推来求

用 f( i,j ) 表示从  i、j  这个格子往上数最多有多少个连续的 1

如果第 ( i,j ) 这个格子是 0 的话,就一个都没有

如果第 ( i,j ) 这个格子是 1 的话,它的值应该是当前这个格子的 1,加上当前这个格子上面这个格子往上数的 1 的个数

每个格子往上数的 1 的个数可以用一个数组去递推出来,预处理的时间复杂度是 O( n^2 )

再枚举,整个算法的时间复杂度就是 O( n^3 )

这个过程如何通过单调栈优化为 O( n^2 )?

链表与邻接表、栈与队列、单调栈、单调队列、kmp 算法_小雪菜本菜的博客-CSDN博客

假设当前枚举的是第 j 个格子,当宽度是 1 的时候, 第 j 列的高度应该是 h[ j ],把第一列的 h[ j ] 加上(先不考虑横坐标)

当宽度是 2 的时候,如果比 h[ j ] 高的话,取 min 还是 h[ j ],所以还是加上 h[ j ];当宽度是 3 的时候,发现高度不如 h[ j ],此时就会取新的最小值 h[ k ],然后把高度更新成 h[ k ],再继续往前,每次都是把高度和当前这一列高度取一个 min,然后把当前这个 min 累加起来

h[ k ] 是左边第一个比 h[ j ] 小的数,一旦找到第一个比 h[ j ] 小的数的时候,需要把高度和 h[ j ] 取一个 min,由于它比 h[ j ] 小,所以高度就会更新成 h[ k ],每次把 h[ j ] 和 h[ k ] 取一个 min,再把 min 累加起来

当我们在算第 j 列的时候,一旦找到了一列比 h[ j ] 要小的时候,再往前处理的情况,和处理 h[ k ] 的情况是一样的,所以就没有必要重新算一遍了,直接把算第 k 列的答案拿过来累加起来就可以了,使用单调栈可以把这一步的时间复杂度控制在 O( n )

先枚举行,枚举完行,再枚举列,在枚举列的时候,边枚举边找每一个数左边比它小的这个数,这个过程时间复杂度是 O(n),整个算法的时间复杂度再乘上枚举行的次数就是 n^2

单调栈:找每一个左边第一个比它小的数

单调队列:找滑动窗口里面的最值

class Solution {
public:
    int numSubmat(vector<vector<int>>& mat) {
        //n是行数 m是列数 
        int n = mat.size(),m = mat[0].size();
        //预处理f数组
        vector<vector<int>> f(n,vector<int>(m));  
        //求f数组
        for(int i = 0;i < n;i++ )
            for(int j = 0;j < m;j++ )
                //如果mat[i][j]是1的话
                if(mat[i][j]) {
                    f[i][j] = 1;
                    //如果i大于0,加上上一行的数
                    if(i) f[i][j] += f[i - 1][j]; 
                }
        //res表示答案
        int res = 0;
        //先枚举行
        for(int i = 0;i < n;i++ ) {
            //对于每一个行开一个单调栈 要存储两个值:不仅要存这个下标 还要存这个和
            stack<pair<int,int>> stk;
            //栈里面不一定所有元素都被记录到,可能某些元素记录不到
            for(int j = 0;j < m;j++ ) {
                //找到比当前数小的第一个数 栈不为空 如果栈顶元素大就直接删除
                while(stk.size() && f[i][stk.top().first] >= f[i][j]) stk.pop();
                int s = 0;
                //如果栈里面有元素的话
                if(stk.size()) {
                    //加上栈顶元素之前累加的值
                    s += stk.top().second;
                    //此时栈顶是第一个比当前数小的这一列,栈顶元素就是满足条件的数
                    //从栈顶到当前这个数之间,所有的列都是比当前这一列大的
                    //一共有j - stk.top().first列 每一列都是f[i][j]这么多个矩形
                    s += (j - stk.top().first) * f[i][j];
                } else {
                    //如果栈为空,说明前面没有任何一列比当前这一列小
                    //一共有j + 1列 每一列都是f[i][j]这么多个矩形
                    s += (j + 1) * f[i][j];
                }
                stk.push({j,s});
                res += s;
            }
        }
        return res;
    }
};

最多 k 次交换相邻数位后得到的最小整数

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

qiuqiuyaq

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

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

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

打赏作者

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

抵扣说明:

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

余额充值