• CodeForces


    前序

    由于我个人问题对本题做一个题解记录一下
    在这里插入图片描述
    题目在输出的时候做出了一个 l1l1 and l2l2 长度范围的限定,这对我第一的代码中的 for 循环范围产生了一定设定上的偏差,从左端上看 l1l1 and l2l2 是必定满足 1l1l21≤l1,l2,由于这个条件所以左端是不可能达到 nn 这个位置的所以范围应该是 1l1,l2n11≤l1,l2≤n-1

    题目链接

    B-Dreamoon Likes Permutation

    题目类型

    思维、模拟

    题意简述

    给你一段长度为 nn 的序列,要求你将该序列分为两个部分,分别为序列 p1p1p2p2,长度分别为 l1l1l2l2,这两个序列分别满足满足序列中的元素可以通过排列变成连续序列,且元素刚好只出现一次,问能分成多少种,输出每种 l1l1l2l2 的值。

    题解

    first(TLE-3)

    我原先想到的是set解法,将所有的长度枚举一遍,存储在 set 容器中,因为set容器中去重有序的性质,如果最后一个元素与长度不相符,就说明有重复出现是不符合的,或者是元素不是连续的。

    时间复杂度

    • 每个长度需要枚举一遍也就是O(n)
    • 每个次分成的两个序列都需要插入set也就是 O(n)
    • 每次set中count查询出现次数都是 o(log n),个人觉得卡的就是查询
    • 每次还要对set进行清除,set.clear()底层的实现就是通过earse删除,时间复杂度O(n)
      最后总的时间复杂度为O(nlogn)

    代码

    这里给出代码只是一个思路的参考,是TLE的,要注意,应该是可以用 unordered_map 去进行一个优化,但我是懒了

    const int maxn = 2e5+50;
    set<int> ap;
    int a[maxn];
    vector<PII> ans;
    int main(){
        int t;
        for(scanf("%d",&t);t;t--){
            ans.clear();
            int n; RD(n);
            REP(i, n) RD(a[i]);
            for(int len = 1; len <= n - 1; len++){
                ap.clear();
                bool judge = true;
                //cout << "len :" << len << '
    ';
                for(int i = 0; i < len; ++i){
                    ap.insert(a[i]);
                }
                int tmp;
                tmp = *(--(ap.end()));
                if (ap.size() != len || ap.size() != tmp) continue;
                ap.clear();
                for(int i = len; i < n; i++){
                    ap.insert(a[i]);
                }
                tmp = *(--(ap.end()));
                if (ap.size() != n - len || ap.size() != tmp) continue;
                ans.PB(MP(len,n - len));
            }
            if (ans.size() == 0) {
                cout << 0 << '
    ';
            }
            else{
                cout << ans.size() << '
    ';
                for(PII v: ans){
                    cout << v.fi << " " << v.se << '
    ';
                }
            }
        }   
    }
    
    

    second

    官方解答

    在这里插入图片描述
    设ma为a中所有元素的最大值。如果可以将a序列分解为两个排列p1p1 and p2p2,那么ma必须与l1l1l2l2中最大的那一个值相等。
    所以最多有两种情况:

    1. l1=ma,l2=nmal1 = ma, l2 = n - ma
    2. l2=ma,l1=nmal2 = ma, l1 = n - ma

    我们可以分别检查这两种情况,时间复杂度为O(n)。

    代码

    写法一(TLE)

    //判断是否访问过
    const int MAXN = 2e5+50;
    int used[MAXN];
    bool judge_used(int a[], int n){
        
    
        REP(i, n+1) used[i] = 0;
        REP(i, n) used[a[i]] = 1;
        FOR_1(i, 1, n) if (!used[i]){return false;}
        return true;
    
    }
    
    vector<PII> ans;
    const int maxn = 2e5+50;
    int a[maxn];
    
    int main(){
        int t;
        for(scanf("%d", &t); t; t--){
            ans.clear();
            int n, ma = 0; RD(n);
            REP(i, n) { RD(a[i]);  ma = max(ma, a[i]);}
            for(int len1 = 1; len1 <= n - 1; ++len1){
                if (judge_used(a, len1) && judge_used(a + len1, n - len1)){
                    if (ma == len1 || ma == n - len1) ans.PB(MP(len1, n - len1));
                }
            }
            cout << ans.size() << '
    ';
            for(PII v : ans){
                cout << v.fi << " " << v.se << '
    ';
            }
        }
    }
    

    超时的点主要是枚举所有长度的时候超时,所以应该直接判断去做,这里枚举长度所造成的时间复杂度是O(n2n^2)超时是必然的

    正确代码

    //判断是否访问过
    const int MAXN = 2e5+50;
    int a[MAXN];
    int used[MAXN];
    int ans[MAXN][2];
    int ans_cnt;
    bool judge_used(int a[], int n){
        REP(i, n+1) used[i] = 0;
        REP(i, n) used[a[i]] = 1;
        FOR_1(i, 1, n) if (!used[i]){return false;}
        return true;
    
    }
    bool judge(int len1, int n){
        return judge_used(a, len1) && judge_used(a + len1, n - len1);
    }
    
    int main(){
        int t;
        for(scanf("%d", &t); t; t--){
            ans_cnt = 0;
            int n, ma = 0; RD(n);
            REP(i, n) { RD(a[i]);  ma = max(ma, a[i]);}
            if(judge(n - ma,n)) {
                ans[ans_cnt][0] = n - ma;
                ans[ans_cnt++][1] = ma;
            }
            if(ma * 2 != n && judge(ma,n)) {
                ans[ans_cnt][0] = ma;
                ans[ans_cnt++][1] = n - ma;
            }
            printf("%d
    ", ans_cnt);
            for(int i = 0; i < ans_cnt; i++) {
                printf("%d %d
    ", ans[i][0], ans[i][1]);
            }
        }
    }
    
  • 相关阅读:
    【leetcode】面试题 01.04. 回文排列
    【leetcode】将数组分成和相等的三个部分
    【leetcode】杨辉三角
    【leetcode】杨辉三角 II
    【leetcode】判断路径是否相交
    【leetcode】路径总和
    【leetcode】山脉数组的峰顶索引
    053-4
    053-151
    053-272
  • 原文地址:https://www.cnblogs.com/ygbrsf/p/13040457.html
Copyright © 2020-2023  润新知