【力扣】第 461 场周赛 AK题解

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 进去,如果发现相邻相同的,清空 p1p2,重头来,否则继续搞 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;
    }
};