本文正在参与掘金团队号上线活动,点击 查看大厂春招职位
一、题目描述:
今天我们来写一道 Codeforces 上的一道比较有意思的思维题:Codeforces 1029B 创建比赛。
题目大意是给你一个升序数组 ,要你从里面找到一个子序列 满足 都有 ,并且使这个子集的大小最大。
二、思路分析:
乍一看这道题好像很难,在一个集合里面寻找满足条件的子集属于组合问题,而这道题可能的组合个数是 个,显然不能使用朴素的枚举组合来写,这个时候就需要动一点脑筋了。
如果一道题看起来很复杂而且还有解的话,那么它一定有化简的办法,这道题只给出了相邻元素的关系限制,我们可以以此为突破点找到规律。
-
我们不妨设 是要选取的元素中最小的,那么 之前的元素显然不满足条件,所以只需要考虑 之后的元素,而且因为数组是有序的,后续我们只需要考虑 这个条件即可。
-
假设有两个元素满足条件,那么我们一定可以先将较小的那个加入,再加入较大的那个,因为 这个条件会随着加入元素的增大越来越宽松,这样贪心地找下去,我们就能找到以 开始的满足条件的最大子序列。在这里我们也能够发现,用子串来描述这个结果集合会更适合,因为如果 都不满足条件,那后面的就更不可能了,所以不存在选择”断开“的可能。
-
一旦发现选择的子集只可能是子串,答案就呼之欲出了,因为选择不能”断开“,所以这些子串一定不会相邻,那么我们只要从头到尾找到所有满足条件的子串,然后输出最小的子串长度即可。
-
因为是顺序遍历,而且每个新加元素是否满足条件只有前一位有关,我们可以不用存储输入数组,而使用一个变量
pre
来记录上一位,然后循环更新答案即可。需要注意的是只选择一个元素总是满足条件的,所以我们要把ans
初始化为1
。
三、AC 代码:
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int pre = 0;
int ans = 1, cnt = 1;
for (int i = 0; i < n; ++i) {
int num;
cin >> num;
if (num <= pre * 2) {
++cnt;
ans = max(ans, cnt);
} else {
cnt = 1;
}
pre = num;
}
cout << ans << endl;
}
四、总结:
这种”构造满足一定条件的子数组“的题是十分经典的一类题目,变体有很多,比如把上面的条件改成相邻元素小于一定值,做法还是一样的。所以遇到这种有套路的题目我们要及时归纳,做一道题就能做一类题,达到事半功倍的效果。