LeetCode第287场周赛前四题题解(函数类二分的练习)

- 本人的LeetCode账号:魔术师的徒弟,欢迎关注获取每日一题题解,快来一起刷题呀~

1 转化时间需要的最小时间数

  思路比较简单,模拟即可,考虑到如果correct的分钟数小于current的分钟数,则只增加correct - current - 1个小时数,然后让correct的分钟数+60,然后再算current的分钟数变到correct的分钟数需要的时间;如果correcct的分钟数大于等于current的分钟数,则增加correct - current个小时数,然后计算current的分钟数边到correct的分钟数用的时间即可。

class Solution {
public:
    int convertTime(string current, string correct) 
    {
        int res = 0;
        int hour1 = stoi(current.substr(0, 2));
        int hour2 = stoi(correct.substr(0, 2));
        int min1 = stoi(current.substr(3, 2));
        int min2 = stoi(correct.substr(3, 2));
        if (min2 < min1)
        {
            res += hour2 - hour1 - 1;
            min2 += 60;
        }
        else 
        {
            res += hour2 - hour1;
        }    
        int gap = min2 - min1;
        res += gap / 15;
        gap %= 15;
        res += gap / 5;
        gap %= 5;
        res += gap;
        return res;
    }
};

2 找出输掉零场或一场比赛的玩家

  本题的思路也很简单,用一个哈希表统计选手失败的次数,如果是胜者,则myhash[winner];,表示只是插入winner,其失败次数为0,为了保证有序,我们可以用一个map<int, int>来存储,第一个键值表示号,第二个键值表示失败次数,最后遍历一遍哈希表即可。

class Solution {
public:
    vector<vector<int>> findWinners(vector<vector<int>>& matches) 
    {
        map<int, int> myhash;
        for (const auto& match : matches)
        {
            myhash[match[0]];
            myhash[match[1]]++;
        }
        vector<vector<int>> ans(2);
        for (auto p : myhash)
        {
            if (p.second == 0)
            {
                ans[0].push_back(p.first);
            }
            else if (p.second == 1)
            {
                ans[1].push_back(p.first);
            }
        }
        return ans;    
    }
};

  反思:其实我比赛思路是用unordered_map + set去重,不过写起来比较长,延误了一些时间。

3 每个小孩最多能分到多少糖果

  本题是比较典型的函数类二分题目,随着每个小孩能够分到的糖果树的增加,它所映射到的能够分给的小孩数会单调减少,所以会把每个小孩分到的糖果数分成两个区间,左边区间是能够分给的小孩数大于k,右边区间是能够分给的小孩书小于等于k,所以我们可以找能够分给的小孩数量>=k这一区间的右端点,用二分就可以做到,注意题目条件,每个小孩最多拿走一堆糖果,即二分的有边界是*max_element(candies.begin(), candies.end())

class Solution {
public:
    typedef long long LL;
    int maximumCandies(vector<int>& candies, long long k) 
    {
        LL sum = accumulate(candies.begin(), candies.end(), LL(0));
        if (sum < k) return 0;
        if (sum == k) return 1;
        int l = 0, r = *max_element(candies.begin(), candies.end());
        while (l < r)
        {
            int mid = (l + r + 1) >> 1;
            LL cnt = 0;
            for (int c : candies)
            {
                cnt += c / mid;
            }
            if (cnt >= k)
            {
                l = mid;
            }
            else 
            {
                r = mid - 1;
            }
        }
        return r;
    }
};

  反思:考场上这个题做的很慢,因为函数类二分不太熟悉,而且这题必须得用左区间的右端点,因为右区间的左端点可能不是最大的糖果数,可能是满足给分k个小孩的最小糖果数,我们要找的是够分k个小孩的最大糖果数,所以是左区间的右端点。

相似题目练习

class Solution {
public:
    int minimumSize(vector<int>& nums, int maxOperations) 
    {
        // 单个袋子中的球的数目的最大值为y
        // 很容易发现y增加,操作次数减少
        // 我们要找操作次数小于等于maxOperations的最小的y
        // 即右区间 op(y) <= maxOperations的左端点
        // 操作次数的获得公式是 ∑ (nums[i] - 1) / y 对i求和
        // y的左端点是1 右端点是max_element(nums)
        int l = 1, r = *max_element(nums.begin(), nums.end());
        while (l < r)
        {
            int mid = (l + r) >> 1;
            int op = 0;
            for (auto p : nums)
            {
                op += (p - 1) / mid;
            }
            if (op <= maxOperations) r = mid;
            else l = mid + 1;
        }
        return r;
    }
};
class Solution {
public:
    typedef long long LL;
    int minTime(vector<int>& time, int m) 
    {
        // 做题用时最多的一天用时为T 显然随着T的增加 所用天数m会单调减少
        // 所以这是一个可以用二分解决的问题
        // 最大的痛点在于如何同T得到m 因为这里头有小杨的帮助
        // 因为要求最小的T 可以用贪心策略 不管怎么样先让小杨帮我
        // 然后后续再及时维护用时最长的work
        LL l = 0, r = accumulate(time.begin(), time.end(), (LL)0);
        while (l < r)
        {
            LL mid = (l + r) >> 1;
            // 这个check函数是表示最多一天用时mid 能够在m天内完成
            // 我们找能够早m天内完成的区间的左端点
            if (check(mid, time, m) == true)
            {
                r = mid;
            }
            else l = mid + 1;
        }
        return l;
    }
    bool check(LL limit, const vector<int>& time, int m)
    {
        LL cursum = 0;
        int curmax = time[0];
        // 先把第0天的题找小杨帮上 后续再通过max min来换成这段时间用时最长的题让小杨帮
        int useddays = 1;// 当前是第几天
        for (int i = 1; i < time.size(); ++i)
        {
            int t = time[i];
            // 如果今天还能再做
            if (cursum + min(curmax, t) <= limit)
            {
                cursum += min(curmax, t);
                curmax = max(curmax, t);
            }
            
            // 否则只能到下一天了
            else
            {
                cursum = 0;// 继续让第一题就然小杨帮
                curmax = t;// 当前题就是明天第一题
                useddays++;
            }
        }
        return useddays <= m;
    }
};

4 加密解密字符串

  对于加密部分,我们考虑用一个哈希表,由keys映射到values,这样就能够完成加密。

  对于解密部分,我们可以先把词典中所有允许为答案的字符串全部通过加密函数得到它们加密后的串,然后用一个哈希表统计它们能变化来的加密后的串的数量,解密时直接返回这个数量即可。

class Encrypter {
public:
    Encrypter(vector<char>& keys, vector<string>& values, vector<string>& dictionary) 
    {
        for (int i = 0; i < keys.size(); ++i)
        {
            keytoval[keys[i]] = values[i];
        }
        for (string& wd : dictionary)
        {
            ++cnt[encrypt(wd)];
        }
    }
    
    string encrypt(string word1) 
    {
        string res;
        for (char ch : word1)
        {
            res += keytoval[ch];
        }
        return res;
    }
    
    int decrypt(string word2) 
    {
        if (!cnt.count(word2)) return 0;
        return cnt[word2];
    }
private:
    unordered_map<char, string> keytoval;
    unordered_map<string, int> cnt;
};

  脑筋急转弯不会转,字典树不会调,入土了。

5.扯淡

  函数类二分不熟悉。。。哎,手速也太慢了,上分真是困难啊,不过还好这周没掉分,多练练函数类二分希望下次遇到能快速想到并写出来吧。

  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值