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;
}
原文地址:https://www.cnblogs.com/scl0725/p/12396589.html