• 来自PTA Basic Level的三只小野兽


     

    点我阅读原文

    最近利用闲暇时间做了一下 PTA Basic Level[1] 里的题,里面现在一共有 95 道题,这些题大部分很基础,对于刷倦了 leetcode 的小伙伴可以去里面愉快的玩耍哦。

    这里我挑了三个挺有意思的题来做个简单记录,欢迎和大家一起讨论交流。

    请听题:

    第一题:1009 说反话

    题目描述

    给定一个英语句子,各个单词之间用空格分隔。要求你编写程序,将句中所有单词的顺序颠倒输出。

    输入示例

    Hello World Here I Come

    输出示例

    Come I Here World Hello


    没骗你吧,是不是很简单。这道题我们确实可以很容易的解决,但你的思路是什么呢?你设计的算法很完美么?

    相信会有一部分小伙伴的思路是这样滴: 首先string来保存输入的句子,再split分隔提取单词,将单词保存在vector中,最后倒序输出。

    vector<string> split(string sentence) {
        //将句子中的单词提取,返回
    }
    int main() {
        string sentence;
        cin >> sentence;
        vector<string> allWords = split(sentence);
        for (string word : allWords) {
            cout << word << ' ';
        }
        return 0;
    }

    这样没毛病哈,我第一次就是这么干的。但对于这种简单题,这样做显得太麻烦了,然后我在网上看到了这段代码:

    int main() {
        stack<string> v;
        string s;
        while(cin >> s) v.push(s);
        cout << v.top();
        v.pop();
        while(!v.empty()) {
            cout << " " << v.top();
            v.pop();
        }
        return 0;
    }

    当时真的惊艳到我了,上面代码充分利用了cin以空格分隔各个变量输入的特点,直接提取到了各个单词。

    之后巧妙利用栈后进先出的特点,将单词依次压入stack,最后再依次弹出,就得到了逆序的单词序列,这两行代码用的着实巧妙!

    第二题:1060 爱丁顿数

    题目描述

    据说天文学家爱丁顿为了炫耀自己的骑车功力,定义了一个“爱丁顿数” E ,即满足有 E 天骑车超过 E 英里的最大整数 E。

    现给定某人 N 天的骑车距离,请你算出对应的爱丁顿数 E(≤N)。

    输入第一行给出一个正整数 N (≤10^5),即连续骑车的天数;第二行给出 N 个非负整数,代表每天的骑车距离。

    在一行中输出 N 天的爱丁顿数。

    输入示例

    10

    6 7 6 9 3 10 8 2 7 8

    输出示例

    6


    看到 N (≤10^5) 我以为不能暴力求解,但旺旺没想到,暴力竟然也能过。首先我们来看一下暴力思路是咋样的。

    通过题目我们可以确定 E 的取值范围是 0 ~ N,也就是说我们要在0 ~ N中搜索一个符合条件的尽可能大的数。

    暴力思路就是这样的:

    for (int i = N; i >= 0; i--) {
        //判断i是否符合条件
        if (isOk(i, a, n)) {
            cout << i;
            break;
        }
    }

    这个时间复杂度是 On^2 ,因为外层for的遍历需要 n,判断每个 i 是否符合条件也需要 n。

    要优化其实也很简单。既然是要在0 ~ N的空间中搜索,而且0 ~ N也是单调的,那正好符合二分的使用条件。我们就可以用二分来代替第一层for循环,这样时间复杂度就是 O(nlog^n) 了。

    二分优化后的代码:

    // 判断k是否符合条件
    bool isOk(int k, int a[], int n) {
        int cnt = 0;
        for (int i = 0; i < n; i++)
            if (a[i] > k)
                cnt++;
        return cnt >= k ? true : false;
    }
    int main() {
        int n, a[100001];
        cin >> n;
        for (int i = 0; i < n; i++)
            cin >> a[i];
        int l = 1, r = n;
        while (l <= r) {
            int mid = l + ((r - l) >> 1);
            if (isOk(mid, a, n))
                l = mid + 1;
            else
                r = mid - 1;
        }
        cout << r;
        return 0;
    }

    像这种使用二分优化搜索空间的题型 leetcode 上也有很多,比如 875. 爱吃香蕉的珂珂[2]1011. 在 D 天内送达包裹的能力[3]1231. 分享巧克力[4],对这块不熟悉的小伙伴可以去做做。

    第三题:1070 结绳

    题目描述

    给定若干段绳子,你需要把它们串成一条绳。每次串连的时候,是把两段绳子对折,再套接在一起。这样得到的绳子又被当成是另一段绳子,可以再次对折去跟另一段绳子串连。每次串连后,原来两段绳子的长度就会减半。

    给定 N 段绳子的长度,你需要找出它们能串成的绳子的最大长度。

    输入示例

    8

    10 15 12 3 4 13 1 15

    输出示例

    14


    经典算法中有两类算法特别考验解题思维,一是动态规划,二是贪心思想。

    这道题就是一道简单的贪心题。根据题目的意思,我们需要找到一种串连方式,使得最终得到的绳子的长度最长,也就是使得 N 段绳子损失的长度最小。

    那么如何尽可能减小绳子长度的损失呢?每次尽可能的选用短绳对折连接,从而避免长绳对折,采用这种方式进行连接绳子长度损失是最小的。

    在程序中我们可以使用优先队列priority_queue让队首元素保持值最小,代码如下:

    int main() {
        priority_queue<int, vector<int>, greater<int>> q;
        int n;
        cin >> n;
        for (int i = 0; i < n; i++) {
            int v;
            cin >> v;
            q.push(v);
        }
        while (q.size() > 1) {
            int l1 = q.top(); q.pop();
            int l2 = q.top(); q.pop();
            q.push((l1 + l2)/2);
        }
        cout << q.top();
        return 0;
    }

    对贪心不熟悉的小伙伴可以看看这篇文章:初识贪心思想

    总结

    这三道题不会很难吧,但要写出令人眼前一亮的代码不仅要有扎实的算法基础,还要能够灵活的使用数据结构。所以在做题的时候要多思考,多总结!

    今天是小年,祝大家小年快乐!

    参考资料

    [1]

    PTA Basic Level: https://pintia.cn/problem-sets/994805260223102976/problems/type/7

    [2]

    875. 爱吃香蕉的珂珂: https://leetcode-cn.com/problems/koko-eating-bananas/

    [3]

    1011. 在 D 天内送达包裹的能力: https://leetcode-cn.com/problems/capacity-to-ship-packages-within-d-days/

    [4]

    1231. 分享巧克力: https://leetcode-cn.com/problems/divide-chocolate/

  • 相关阅读:
    BD String
    1114
    1083
    1084
    1108
    1087
    1145
    1217
    1164
    反射
  • 原文地址:https://www.cnblogs.com/huwt/p/12209024.html
Copyright © 2020-2023  润新知