最近利用闲暇时间做了一下 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/
原文地址:https://www.cnblogs.com/huwt/p/12209024.html