• Chapter4枚举,模拟与排序


    Chapter4 枚举,模拟与排序

    +++

    • 枚举

    1.连号区间数 1210

    首先想到的就是暴力枚举,但是复杂度过高会超时。

    接下来就想优化,由于本题输入的是不重复的连续的序列,因此找到连号区间的性质

    即(a~b)的最大值减去最小值等于b - a,降低暴搜检查合法序列的复杂度

    //复杂度O(n²)
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 10010;
    int a[maxn];
    int n, res;
    
    int main()
    {
        cin >> n;
        for(int i = 0; i < n; i++ ) scanf("%d", a + i);
        
        for(int i = 0; i < n; i++ )//枚举区间左端点
        {
            int Max = -1e9;
            int Min = 1e9;
            for(int j = i; j < n; j++ )//枚举区间右端点
            {
                Max = max(Max, a[j]);
                Min = min(Min, a[j]);
                if(Max - Min == j - i) {res++;continue;}
            }
        }
        
        cout << res << endl;
        return 0;
    }
    
    2.递增三元组 1236

    首先观察数据范围,可以知道这题暴力做法必定超时,因此就想基于暴力做法的优化做法。

    由数据范围可以知道我们最多只可以枚举一个数组元素,因此枚举处于中间位置的数组b,因为当b[]元素确定是,a与c的对应元素范围就会缩小。

    针对每一个b[i],我们可以知道只需要找到a[]中比b[i]小的元素个数和c[]中比b[i]大的元素个数,两个数相乘就是b[i]对结果的贡献值,最后累加所有贡献值就是答案。

    因为结果范围过大,有可能会爆int,因此用long long 来声明结果res

    //前缀和AC CODE
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long LL;//防止结果爆int
    
    const int maxn = 1e5 + 10;
    int a[maxn], b[maxn], c[maxn];
    int cnt[maxn], s[maxn];//s是cnt的前缀和数组,cnt是统计a[],c[]中每个数字出现的个数
    int as[maxn], cs[maxn];//比b[]中某个元素大或者小的数字个数
    
    int main()
    {
        int n;
        cin >> n;
        
        //输入
        for(int i = 0; i < n; i++ ) scanf("%d", a + i), a[i]++;//防止计算前缀和数组是数组越界,下同
        for(int i = 0; i < n; i++ ) scanf("%d", b + i), b[i]++;
        for(int i = 0; i < n; i++ ) scanf("%d", c + i), c[i]++;
        
        //计算as
        for(int i = 0; i < n; i++ ) cnt[a[i]]++;
        for(int i = 1; i < maxn; i++ ) s[i] = s[i - 1] + cnt[i];
        for(int i = 0; i < n; i++ ) as[i] = s[b[i] - 1];//统计比b[]小的数字个数
        
        memset(cnt, 0, sizeof cnt);
        memset(s, 0, sizeof s);
        
        for(int i = 0; i < n; i++ ) cnt[c[i]]++;
        for(int i = 1; i < maxn; i++ ) s[i] = s[i - 1] + cnt[i];
        for(int i = 0; i < n; i++ ) cs[i] = s[maxn - 1] - s[b[i]];//统计比b[]大的数字个数
        
        LL res = 0;
        for(int i = 0; i < n; i++ ) res += (LL)as[i] * cs[i];
        
        cout << res << endl;
        return 0;
    }
    
    //错误代码,还没找到问题
    //样例是对的
    //待解决
    
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long LL;
    
    const int N = 1e5 + 10;
    int a[N], b[N], c[N];
    int n;
    
    LL acnt(int u)//二分计算a[]中小于b[i]的元素个数
    {
        int L = 0, R = n;
        while(L < R)
        {
            int mid = (L + R) >> 1;
            
            if(a[mid] >= u) R = mid;
            else L = mid + 1;
        }
        
        return R;
    }
    
    LL ccnt(int u)//二分计算c[]中大于b[i]的元素个数
    {
        int L = 0, R = n;
        while(L < R)
        {
            int mid = (L + R + 1) >> 1;
            
            if(c[mid] <= u) L = mid;
            else R = mid - 1;
        }
        
        return n - R;
    }
    
    
    int main()
    {
        cin >> n;
        
        for(int i = 0; i < n; i++ ) scanf("%d", a + i);
        for(int i = 0; i < n; i++ ) scanf("%d", b + i);
        for(int i = 0; i < n; i++ ) scanf("%d", c + i);
        
        //排序,为二分做准备
        sort(a, a + n);
        sort(c, c + n);
        
        LL res = 0;
        for(int i = 0; i < n; i++ )
            res += acnt(b[i]) * ccnt(b[i]);
            
        cout << res << endl;
        return 0;
    }
    
    //STL二分函数AC CODE
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long LL;
    
    const int N = 1e5 + 10;
    int a[N], b[N], c[N];
    int n;
    
    int main()
    {
        cin >> n;
        
        for(int i = 0; i < n; i++ ) scanf("%d", a + i);
        for(int i = 0; i < n; i++ ) scanf("%d", b + i);
        for(int i = 0; i < n; i++ ) scanf("%d", c + i);
        
        //从小到大排序,为二分做准备
        sort(a, a + n);
        sort(c, c + n);
        
        LL res = 0;
        for(int i = 0; i < n; i++ )
        {
            LL x = lower_bound(a, a + n, b[i]) - a;
            LL y = n - (upper_bound(c, c + n, b[i]) - c);
            
            res += x * y;
        }
            
        cout << res << endl;
        return 0;
    }
    
    • 模拟

    1.特别数的和 1245
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    
    int main()
    {
        int n, sum = 0;
        cin >> n;
        for(int i = 1; i <= n; i++ )
        {
            int x = i;
            while(x)
            {
                int t = x % 10;
                x /= 10;
                if(t == 2 || t == 0 || t == 1 || t == 9)
                {
                    sum += i;
                    break;   
                }
            }
        }
        
        cout << sum << endl;
        return 0;
    }
    
    2.错误票据 1204
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 10010;
    int a[N];
    int n, j;
    
    int main()
    {
        cin >> n;
        
        //奇怪的输入方式
        for(int i = 0; i < n; i++ )
            while(cin >> a[j])
                j++;
    
        sort(a, a + j);
        
        int res1, res2;
        for(int i = 1; i < j; i++ )
            if(a[i] >= a[i - 1] + 2)   
                res2 = a[i - 1] + 1;
            else if(a[i] == a[i - 1])   
                res1 = a[i];
        
        cout << res2 << " " << res1 << endl;
        return 0;
    }
    
    //stringstream 处理
    
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <sstream>
    
    using namespace std;
    
    const int N = 10010;
    int n;
    int a[N];
    
    int main()
    {
        int cnt;
        cin >> cnt;
        string line;
        
        getline(cin, line);//除回车
        for(int i = 0; i < cnt; i++ )
        {
            getline(cin, line);
            stringstream ssin(line);
            
            while(ssin >> a[n])  n++;
        }
        
        sort(a, a + n);
        
        int res1, res2;
        for (int i = 1; i < n; i ++ )
            if (a[i] == a[i - 1]) res2 = a[i];  // 重号
            else if (a[i] >= a[i - 1] + 2) res1 = a[i] - 1; // 断号
    
        cout << res1 << ' ' << res2 << endl;
    
        return 0;
        
    }
    
    3.回文日期 466 (枚举 + 模拟)
    //枚举日期的话会很麻烦,所以就可以枚举回文数然后判断是不是合法日期
    
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    int day_[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    int res;
    
    bool check_valid(int u)
    {
        int year = u / 10000;
        int month = u % 10000 / 100;
        int dayday = u % 100;
        
        if(month <= 0 || month > 12) return false;
        if(month != 2 && (dayday > day_[month] || dayday <= 0)) return false;
        
        if(month == 2)//特判2月
        {
            if(year % 100 && year % 4 == 0 || year % 400 == 0)
            { 
                if(dayday <= 0 || dayday > 29)
                    return false;
            }
            else if(dayday <= 0 || dayday > 28)  return false;
        }
        return true;
    }
    
    int main()
    {
        int day1, day2;
        cin >> day1 >> day2;
        
        for(int i = 1000; i <= 9999; i++ )
        {
            int day = i, x = i;
            //转换成8位回文数
            for(int j = 1; j <= 4; j++ )    day = day * 10 + x % 10, x /= 10;
            
            if(day1 <= day && day <= day2)
                if(check_valid(day))    res++;
        }
        
        cout << res << endl;
        return 0;
    }
    
    • 排序

    1.归并排序 787
    //双指针
    
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    
    using namespace std;
    
    const int N = 1e5 + 10;
    int a[N], tmp[N];
    
    void merge_sort(int q[], int l, int r)
    {
        if(l >= r)  return;
        
        int mid = l + r >> 1;
        
        merge_sort(q, l, mid);
        merge_sort(q, mid + 1, r);
        
        int k = 0, i = l, j = mid + 1;
        while(i <= mid && j <= r)
            if(q[i] <= q[j]) tmp[k++] = q[i++];
            else tmp[k++] = q[j++];
        while(i <= mid) tmp[k++] = q[i++];
        while(j <= r) tmp[k++] = q[j++];
        
        for(i = l, j = 0; i <= r; i++, j++ ) a[i] = tmp[j];
        
    }
    
    int main()
    {
        int n;
        cin >> n;
        for(int i = 0; i < n; i++ ) scanf("%d", a + i);
        
        merge_sort(a, 0, n - 1);
        
        for(int i = 0; i < n; i++ ) printf("%d ", a[i]);
        
        return 0;
    }
    
    • 习题

    1.移动距离 1219
    //画图找规律
    
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    
    int main()
    {
        int u, m, n;
        cin >> u >> m >> n;
        m--, n--;
        
        int x1 = m / u, x2 = n / u;
        int y1 = m % u, y2 = n % u;
        
        if(x1 % 2)  y1 = u - 1 - y1;
        if(x2 % 2)  y2 = u - 1 - y2;
        
        cout << abs(x1 - x2) + abs(y1 - y2) << endl;
        return 0;
    }
    
    2.日期问题 1229

    枚举所有数字,筛选出合法的日期,接着再筛选出来符合条件的日期

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    
    using namespace std;
    
    int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    int a, b, c;
    
    bool check_valid(int year, int month, int day)//检测日期是否合法
    {
        if(month <= 0 || month > 12)    return false;
        if(month != 2)
        {
            if(day <= 0 || day > days[month])   return false;
        }
        if(month == 2)
        {
            if(year % 4 == 0 && year % 100 || year % 400 == 0)
            {
                if(day <= 0 || day > 29)    return false;
            }
            else if(day <= 0 || day > 28)   return false;
        }
        
        return true;
    }
    
    int main()
    {
        scanf("%d/%d/%d", &a, &b, &c);//格式化输入的优势
        
        for(int i = 19600101; i <= 20591231; i++ )
        {
            int year = i / 10000, month = i % 10000 / 100, day = i % 100;
            if(check_valid(year, month, day))
            {
                   if(year % 100 == a && month == b && day == c || //年月日
                   day == a && month == b && year % 100 == c || //日月年
                   month == a && day == b && year % 100 == c    //月日年
                     )
                        printf("%d-%02d-%02d
    ", year, month, day);
            }
        }
        
        return 0;
    }
    
    3.航班问题 1231(模拟,数学)

    对于这种格式化输入输出的题,应该灵活应用printf, scanf

    思路:

    规范小时的减法有点难实现,所以不如统一转换成秒数来计算

    sscanf的格式化读入非常有效

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    int get_seconds(int h, int m, int s)
    {
        return h * 3600 + m * 60 + s;
    }
    
    int get_time()
    {
        string line;
        getline(cin, line);
    
        if (line.back() != ')') line += " (+0)";
    
        int h1, m1, s1, h2, m2, s2, d;
        sscanf(line.c_str(), "%d:%d:%d %d:%d:%d (+%d)", &h1, &m1, &s1, &h2, &m2, &s2, &d);
    
        return get_seconds(h2, m2, s2) - get_seconds(h1, m1, s1) + d * 24 * 3600;
    }
    
    int main()
    {
        int n;
        scanf("%d", &n);
        string line;
        getline(cin, line);     // 忽略掉第一行的回车
        while (n -- )
        {
            int time = (get_time() + get_time()) / 2;
            int hour = time / 3600, minute = time % 3600 / 60, second = time % 60;
            printf("%02d:%02d:%02d
    ", hour, minute, second);
        }
    
        return 0;
    }
    
    4.外卖店优先级 1241(★★)
    #include <bits/stdc++.h>
    
    #define x first
    #define y second
    
    using namespace std;
    
    typedef pair<int, int> PII;
    
    const int N = 1e5 + 10;
    bool st[N];
    int score[N], last[N];
    PII order[N];//存放输入数据,好处理
    
    int main()
    {
        int n, m, T;
        cin >> n >> m >> T;
        
        for(int i = 0; i< m; i++ )  scanf("%d%d", &order[i].x, &order[i].second);
        
        sort(order, order + m);
        
        for(int i = 0; i < m;)
        {
            int j = i;
            while(order[i] == order[j] && j < m)   j++;//
            int t = order[i].x, id = order[i].y, cnt = j - i;
            i = j;//
            
            score[id] -= t - last[id] - 1;
            if(score[id] < 0)   score[id] = 0;
            if(score[id] <= 3)  st[id] = false;
            if(score[id] > 5)   st[id] = true;//在此之前处理的是t时刻之前的订单
            
            score[id] += cnt * 2;
            if(score[id] > 5)   st[id] = true;
            
            last[id] = t;
        }
        
        for(int i = 1; i <= n; i++)
        {
            if(last[i] < T)//如果在last[i]出现过之后没有再有订单的情况
            {
                score[i] -= T - last[i];
                if(score[i] <= 3) st[i] = false;
            }
                
        }
        
        int res = 0;
        for(int i = 1; i <= n; i++ ) res += st[i];//bool自动转换成int
        cout << res << endl;
        
        return 0;
        
    }
    
    5.逆序对的数量 788(归并排序)
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    
    using namespace std;
    
    const int N = 1e5 + 10;
    int a[N], tmp[N];
    long long res;
    
    void merge_sort(int q[], int l, int r)
    {
        if(l >= r)  return;
        
        int mid = l + r >> 1;
        
        merge_sort(q, l, mid);
        merge_sort(q, mid + 1, r);
        
        int k = 0, i = l, j = mid + 1;
        while(i <= mid && j <= r)
            if(q[i] <= q[j]) tmp[k++] = q[i++];
            else tmp[k++] = q[j++], res += mid - i + 1;
        while(i <= mid) tmp[k++] = q[i++];
        while(j <= r) tmp[k++] = q[j++];
        
        for(i = l, j = 0; i <= r; i++, j++ ) a[i] = tmp[j];
        
    }
    
    int main()
    {
        int n;
        cin >> n;
        for(int i = 0; i < n; i++ ) scanf("%d", a + i);
        
        merge_sort(a, 0, n - 1);
        
        cout << res << endl;
        
        return 0;
    }
    
  • 相关阅读:
    编译原理知识点总括
    操作系统知识概括
    计算机网络(谢希仁)第六版第六章
    计算机网络(谢希仁)第六版第五章
    计算机网络(谢希仁)第六版第三章
    计算机网络(谢希仁)第六版第二章
    计算机网络(谢希仁)第六版第一章
    phpcms v9 模板标签说明整理
    HTML 5终于定稿,八年后我们再一次谈谈怎么改变世界
    jquery toggle 替换的实现
  • 原文地址:https://www.cnblogs.com/scl0725/p/12396589.html
Copyright © 2020-2023  润新知