LeetCode双周赛第70场,考察你的基本功

作者 | 梁唐

出品 | 公众号:Coder梁(ID:Coder_LT)

大家好,我是梁唐。

昨天有一场LeetCode双周赛,不知道有没有小伙伴参加,老梁连夜肝出了题解。

这场比赛是由六方云赞助,并提供了小霸王游戏机等精美礼品……只要打进前五就可以玩了呢……

话不多说,让我们一起看看题目吧。

打折购买糖果的最小开销

一家商店当中有n个糖果,商店规定每买两枚糖果就可以免费赠送一颗,赠送的糖果大小不能超过购买的这两颗的任意一颗。

现在我们要买下商店中所有的糖果,请问最少需要多少花费?

解法

数据范围很小,我们可以随意操作。

不难发现这是一道典型的贪心问题。

首先考虑对糖果进行排序,显然对于最大的两颗糖果是必须要花钱买的。而买下了最大的两颗糖果之后,我们一定是免费获得第三大小的糖果最优。如此我们可以循环往复操作,直到买完所有的糖果。

代码如下:

int main(){
    int cur = 0, ans = INT_MIN, cursum = 0;
    char c='0';
    do{
        while(c == ' '){
            c = getchar();
        }
        cin >> cur;
        cursum += cur;
        ans = max(ans, cursum);
        if(cursum < 0){
            cursum = 0;
        }
        c = getchar();
    }while(c != '\n');
    cout << ans << endl;
    return 0;
}

统计隐藏数组数目

给定一个长度为n的数组diff,它表示原数组的差值,其中diff[i] = hidden[i+1] - hidden[i]

现在给定原数组的上下界lowerupper,要求原数组的所有元素必须在区间[lower, upper]之间。请问,这样的原数组一共有多少种构成的方式,如果不存在可能,返回0.

解法

由于我们已经知道了原数组中每两个相邻元素的差值,也就是说只要我们确定了其中任意一个数字,就可以确定其他的。

进而我们可以想到,原数组的最大和最小值的差值也是确定的。我们要做的就是保证原数组的最大值不超过upper,最小值不低于lower。我们假设原数组的最大最小值的差值是gap,那么答案就是upper - lower - gap + 1

最简单的做法就是给原数组的第0位一个假定的值,然后求最大最小值计算gap。当然我们也可以不这么做,直接求解。为了方便理解, 我们可以简单做个图,以样例[3,-4,5,1,-2]为例。作图之后得到:

我们来思考最大值和最小值之间的关系,如果最小值出现在最大值左侧,gap体现在图中就是一系列递增得到的顺差:

反之,如果最小值出现在最大值的右侧,那么gap就是通过一系列下降得到的逆差:

我们只需要同时维护一下最大的顺差和逆差,其中绝对值最大(顺差大于0,逆差小于0)的那个就是答案。

求最大顺差和最大逆差用到了求数组区间最大和的思路:维护一个tmp值,保存中间结果。每次读入新值时和tmp相加,接着判断tmp大小。当tmp小于0时,说明之前的序列已经不构成增益,舍弃,将tmp置为0。如此,中途出现的tmp最大值即为答案。

class Solution {
public:
    int numberOfArrays(vector<int>& diff, int lower, int upper) {
        int n = diff.size();
        // maxi 记录顺差, mini 记录逆差
        long long tmp = 0, maxi = INT_MIN, tmpi = 0, mini = INT_MAX;
        for (int i = 0; i < n; i++) {
            // tmp记录当前累加的顺差
            tmp += diff[i];
            // tmpi记录当前累加的逆差
            tmpi += diff[i];
            // 顺差大于0,所以要记录最大值
            maxi = max(maxi, tmp);
            // 逆差小于0,记录最小值
            mini = min(mini, tmpi);
            // 如果tmp小于0,那么清零
            tmp = max(0LL, tmp);
            // 如果tmpi大于0,则清零
            tmpi = min(0LL, tmpi);
        }
        long long gap = max(maxi, abs(mini));
        return max(0LL, (long long)upper - (long long)lower - gap + 1);
    }
};

价格范围内最高排名的 K 样物品

给定一个 n x m的迷宫grid,迷宫内有墙和物品,grid[i][j] = 0,表示i, j位置为墙,大于0为商品,表示商品的价格。

我们无法移动到墙的位置,每次移动到商品可以拿取商品。

接着给定起点以及价格范围和一个整数k,我们只会考虑价格范围在lowerupper之间的商品,且最多只会拿取k个。当有超过k个商品符合要求时,会根据以下关键字进行排序:

距离:定义为从 start 到一件物品的最短路径需要的步数(较近 距离的排名更高)。价格:较低 价格的物品有更高优先级,但只考虑在给定范围之内的价格。行坐标:较小 行坐标的有更高优先级。列坐标:较小 列坐标的有更高优先级。

最后要求返回排名最高的k件商品,如果小于k个,则全部返回。

解法

由于需要走迷宫,且需要求每一个位置的最短距离,所以考虑使用宽度优先搜索。

使用宽度优先搜索求出每一个商品的最短距离,然后维护符合价格区间要求的商品再进行排序即可。

需要注意在搜索时需要做好去重,一方面为了防止答案重复记录,另外一方面防止死循环导致超时。

AC代码:

// 创建结构体存储答案
struct P {
    int dis, price, x, y;
    P() {}
    P(int d, int p, int _x, int _y): dis(d), price(p), x(_x), y(_y) {}
};

class Solution {
public:
    vector<vector<int>> highestRankedKItems(vector<vector<int>>& grid, vector<int>& pricing, vector<int>& start, int k) {
        typedef pair<int, int> pii;
        vector<P> ans;
        queue<P> que;

        map<pii, int> mp;
        int lower = pricing[0], upper = pricing[1];
        que.push(P(0, grid[start[0]][start[1]], start[0], start[1]));
        // 方向数组用来遍历上下左右四个方向
        int dir[4][2] = {{0, 1}, {0, -1}, {-1, 0}, {1, 0}};
        int n = grid.size(), m = grid[0].size();
        set<pii> st;

        while (!que.empty()) {
            auto u = que.front();
            que.pop();
            pii poi = make_pair(u.x, u.y);
            // 如果价格满足要求,且之前没有拿到过,放入候选答案数组ans
            if (u.price >= lower && u.price <= upper && st.count(poi) == 0) {
                ans.push_back(u);
                st.insert(poi);
            }
            // 对四个方向进行遍历
            for (int i = 0; i < 4; i++) {
                int x = u.x + dir[i][0], y = u.y + dir[i][1];
                // 超界判定
                if (x < 0 || x >= n || y < 0 || y >= m) continue;
                // 墙判定
                if (grid[x][y] == 0) continue;
                pii poi = make_pair(x, y);
                // 是否遍历过判定
                if (mp.count(poi) && mp[poi] <= u.dis+1) continue;
                mp[poi] = u.dis+1;
                que.push(P(u.dis+1, grid[x][y], x, y));
            }
        }

        // 对ans数组进行多关键字排序,用到了匿名函数
        sort(ans.begin(), ans.end(), [](auto &x, auto & y)->bool {
            if (x.dis != y.dis) return x.dis < y.dis;
            if (x.price != y.price) return x.price < y.price;
            if (x.x != y.x) return x.x < y.x;
            return x.y < y.y;
        });

        vector<vector<int>> ret;
        for (int i = 0; i < min(k, (int)ans.size()); i++) {
            vector<int> cur = {ans[i].x, ans[i].y};
            ret.push_back(cur);
        }
        return ret;
    }
};

分隔长廊的方案数

在图书馆里有一个长廊,当中要摆放若干椅子和若干植物。用一个字符串进行表示,其中S表示座位,P表示植物。

要求将这些椅子和植物分组,保证每组当中必须有两张椅子,植物数量可以随意,要求满足条件的方案数。

解法

显然这是一道数学题,由于要求每一个分组内恰好有两张椅子,我们假设在第三张椅子出现之前存在的植物数量为x。x个植物一共有x+1个间隙,那么对于当前分组来说一共存在x+1种划分方法。

我们只需要分别求出每一个分组的划分方法,然后乘在一起即可。

注意由于要对1e9+7取模,取模之后再做乘法可能会超过int范围, 所以得使用long long

还有一个trick是最后一个分组可能不一定满足两张椅子的要求,要对此进行特判。

AC代码:

class Solution {
public:
    int numberOfWays(string corr) {
        int n = corr.size();
        // cont表示出现的椅子数量,conp表示连续出现的植物数量,仅仅需要考虑出现两张椅子之后的情况
        int cont = 0, conp = 0;
        long long ret = 1;
        long long mod = 1e9+7;
        for (int i = 0; i < n; i++) {
            // 如果当前是植物,当椅子数量小于2时不做处理,等于2时+1
            if (corr[i] == 'P') {
                if (cont < 2) continue;
                else conp++;
            }else {
                // 椅子为0时直接+1
                if (cont == 0) {
                    cont++;
                // 椅子为1时满足分组要求,将植物数量清零
                }else if (cont == 1) {
                    cont++;
                    conp = 0;
                }else {
                    // 当有两张椅子时,表示进行到下一个分组,将椅子数量置为1
                    cont = 1;
                    // 维护答案
                    ret = ret * (conp + 1);
                    ret = ret % mod;
                }
            }
        }
        // 判断最后一个分组是否满足两张椅子的条件
        if (cont != 2) return 0;
        return ret;
    }
};

原文地址:https://cloud.tencent.com/developer/article/2117538

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340