1. 三段式数组 I
思路 :整个数组都要是严格的"先增后减再增"的话,说明不能有前后一样的,且先有一个波峰(a[i] > a[i-1] && a[i] > a[i+1]
),后有一个波谷(a[i] < a[i-1] && a[i] < a[i+1]
)。
查看代码
class Solution {
public:
bool isTrionic(vector<int>& a) {
int up = 0, down = 0, last = 0;
for(int i=1; i+1<a.size(); i++)
{
if(a[i]==a[i-1]||a[i]==a[i+1]) return false;
if(a[i]>a[i-1]&&a[i]>a[i+1]) up++, last = 0;
if(a[i]<a[i-1]&&a[i]<a[i+1]) down++, last = 1;
}
return up==1&&down==1&&last==1;
}
};
2. 平衡装运的最大数量
思路 :根据题意贪心即可,遍历到第 i 个元素时,更新序列最大值 ma
,如果 a[i]
不等于 ma
的话,说明可以直接划分,cnt++
,重置 ma = 0
。
查看代码
class Solution {
public:
int maxBalancedShipments(vector<int>& w) {
int ma = 0, cnt = 0;
for(int i=0; i<w.size(); i++)
{
ma = max(ma,w[i]);
if(ma!=w[i])
{
cnt++;
ma = 0;
}
}
return cnt;
}
};
3. 变为活跃状态的最小时间
思路 :经典的二分答案模板题。考虑到 t
越大的时候,字符串中的 #
就越多,自然就越容易满足条件。这里就是一个单调性(t
越大 -> 越容易合法),所以对 t
进行二分,每次判断当前二分到的时间 t
能产生多少的带 #
子串。
怎么统计数量呢?考虑有一个字符串 a#aa#a
,我们从 1 开始记下标,那么 #
的位置就是 2 和 5。统计的时候,对于第一个 #
,包含它但不包含后续 #
的数量就是 2 * (5 - 2) = 6
,表示包含第一个 #
的区间,左端点有两种取法,右端点有 (5 - 2 = 3)
种取法。对于第二个 #
,同样的,有 5 * 2 = 10
种,所以一共是 6 + 10 = 16
种,这样就不重不漏地找到了所有子串的数量。如果 16 >= k
,那么就是合法的,可以继续二分缩小右端点 (r = mid - 1)
,否则说明太小了要扩大些,即扩大左端点 (l = mid + 1)
。
查看代码
class Solution {
public:
using ll = long long;
bool check(int t, vector<int>& order, int k, int n)
{
vector<int> id;
for(int i=0; i<=t; i++) id.push_back(order[i]+1);
sort(id.begin(),id.end());
ll cnt = 0;
for(int i=0; i<id.size(); i++)
{
ll L = id[i], R = (i+1==id.size()?n-id[i]+1:id[i+1]-id[i]);
cnt += L*R;
if(cnt>=k) return true;
}
return false;
}
int minTime(string s, vector<int>& order, int k) {
int n = s.size();
int l = 0, r = n-1, ans = n;
while(l<=r)
{
int mid = (l+r)>>1;
if(check(mid,order,k,n)) ans = mid, r = mid - 1;
else l = mid + 1;
}
return ans==n?-1:ans;
}
};
4. 三段式数组 II
思路 :纯模拟就行了。用三个数组 p1
p2
p3
,分别记录第一段上升,第二段下降和第三段上升的待判断序列。
- 对于
p1
,把当前a[i] > a[i-1]
的全 push 进去,如果发现找不到或者当前a[i] == a[i-1]
,说明不用找了,清空p1
,重头来,否则继续搞p2
; - 对于
p2
,同样的,把a[i] < a[i-1]
的一路 push 进去,如果发现相邻相同的,清空p1
、p2
,重头来,否则继续搞p3
; - 对于
p3
,和p1
一样的逻辑,但是如果发现p3
凑不出来,那也是全清空;否则进入判断。
判断 :取 p1
最大的后缀和 + p2
全部(去掉和 p1
p3
重合部分)+ p3
的最大的前缀和即是本轮答案。比较更新每次判断的最大值即可。
查看代码
class Solution {
public:
using ll = long long;
ll cal(vector<int> &p){
ll cur = p[0]+p[1], ma = cur;
for(int i=2;i<p.size(); i++) cur += p[i], ma = max(cur,ma);
return ma;
}
ll sol(vector<int> &p1, vector<int> &p2, vector<int> &p3){
reverse(p1.begin(),p1.end());
ll sum = cal(p1);
reverse(p1.begin(),p1.end());
for(int i=1; i+1<p2.size(); i++) sum += p2[i];
sum += cal(p3);
return sum;
}
ll maxSumTrionic(vector<int>& a) {
vector<int> p1,p2,p3;
ll ans = -1e18;
int i = 1;
p1.push_back(a[0]);
while(i<a.size())
{
// up
while(i<a.size()&&a[i]>p1.back()) p1.push_back(a[i]), i++;
if(i>=a.size()||a[i]==p1.back()||p1.size()==1) {
p1.clear();
p1.push_back(a[i]);
i++;
continue;
}
// down
p2.push_back(p1.back());
while(i<a.size()&&a[i]<p2.back()) p2.push_back(a[i]), i++;
if(i<a.size()&&a[i]==p2.back()||p2.size()==1){
p1.clear(),p2.clear();
p1.push_back(a[i]);
i++;
continue;
}
// up
p3.push_back(p2.back());
while(i<a.size()&&a[i]>p3.back()) p3.push_back(a[i]), i++;
if(p3.size()==1) {
p1 = p3; p2.clear(),p3.clear();
i++;
continue;
}
ans = max(ans,sol(p1,p2,p3));
p1 = p3, p2.clear(),p3.clear();
}
return ans;
}
};