枚举,模拟和排序(蓝桥)

枚举,模拟和排序

1.递增三元组

给定三个整数数组

A=[A1,A2,…AN]
B=[B1,B2,…BN]
C=[C1,C2,…CN]

请你统计有多少个三元组 (i,j,k) 满足:

  1. 1≤i,j,k≤N
  2. Ai<Bj<Ck
数据范围

1≤N≤105
0≤Ai,Bi,Ci≤105

思路:

最简单的思路就是暴力枚举,但是这里行不通,数据范围是105,三重循环就是1015,肯定会超时,根据数据范围可以反推,这里用的算法时间复杂度是nlogn。

那就只能用遍历一个数组了,遍历A和C是一样的,如果遍历A,B和C不独立,没法做。只能遍历B,遍历B的时候那就只需要找A中比B小的个数,C中比B大的个数。

可以考虑的算法有:1.前缀和 2.排序加二分

代码(前缀和):
#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N=100010;
typedef long long LL;


int a[N],b[N],c[N];
int cnt[N],s[N];
int as[N],cs[N];


int main()
{
    int n;
    scanf("%d",&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]++;
    
    for(int i=0;i<n;i++) cnt[a[i]]++;
    for(int i=0;i<N;i++) s[i]=s[i-1]+cnt[i];
    for(int i=0;i<n;i++) as[i]=s[b[i]-1];
    
    memset(cnt,0,sizeof cnt);
    memset(s,0,sizeof s);
    
    for(int i=0;i<n;i++) cnt[c[i]]++;
    for(int i=0;i<N;i++) s[i]=s[i-1]+cnt[i];
    for(int i=0;i<n;i++) cs[i]=s[N-1]-s[b[i]];
   
    LL res=0;
    for(int i=0;i<n;i++)
    {
        res+=(LL)as[i]*cs[i];
    }

    cout<<res<<endl;
    
    return 0;
}
2.特别数的和
题意:

小明对数位中含有 2、0、1、9 的数字很感兴趣(不包括前导 00),在 1到 40 中这样的数包括 1、2、9、10 至 32、39 和 40,共 28 个,他们的和是 574

请问,在 1 到 n中,所有这样的数的和是多少?

数据范围

1≤n≤10000

思路:

这里的数据范围是10^4,暴力枚举一遍再逐个判断不会超时,可以直接用暴力来做。

这里要用到的就是求每一位上的数是多少。

代码:
#include <iostream>
#include <algorithm>

using namespace std;


int main()
{
    int n;
    cin>>n;
    int cnt=0;
    
    for(int i=1;i<=n;i++)
    {
        bool flag=false;
        
        int j=i;
        while(j/10)
        {
            int d=j%10;
           
            if(d==1 || d==2 || d==0 ||d==9) 
            {
                flag=true;
                break;
            }
            j/=10;
            
        }
        if(j==1 || j==2 || j==0 || j==9) flag=true;
        
        if(flag) cnt+=i;
        
    }
    
    cout<<cnt;
    
    return 0;
}
3.错误票据
题意:

输入一个数n,接下来输入n个数(ID),这n个数(ID)是连续的,但ID的开始数码是随机选定的。

因为工作人员疏忽,在录入ID号的时候发生了一处错误,造成了某个ID断号,另外一个ID重号。找出断号的ID和重号的ID。

思路:

俩种做法:1. 排序加查找 2.下标数组,判断是否为空

这里输入的数据有点特殊,是一行行输入的,所有需要先将数据存入string中,再一个个的从string中读入。

代码(排序)
#include <iostream>
#include <algorithm>
#include <sstream>

using namespace std;
const int N=10010;

int a[N];
int n;

int main()
{
    
    int cnt;
    string line;
    
    cin>>cnt;
    
    getline(cin,line);
    while(cnt--)
    {
        getline(cin,line);
        stringstream ssin(line);
        
        while(ssin>>a[n]) n++;
    }
    
    
    int start=0,end=0;
    sort(a,a+n);
    for(int i=1;i<n;i++)
        if(a[i]==a[i-1]) end=a[i];
        else if(a[i]-a[i-1]>1) start=a[i]-1;
        
        
    cout<<start<<' '<<end<<endl;
    
    return 0;
}
4.回文日期
题意:

牛牛习惯用 8 位数字表示一个日期,其中,前 4 位代表年份,接下来 2 位代表月份,最后 2 位代表日期。

给定俩个日期,求指定的两个日期之间(包含这两个日期本身),有多少个真实存在的日期是回文的。

思路:

根据日期的表示方式,可以知道年份的数据范围是1000-10000.可以找到这期中回文的日期,再判断合法。

代码:
#include <iostream>
#include <algorithm>


int days[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};

bool check(int d)
{
    int year=d/10000;
    int month=d%10000/100;
    int day=d%100;
    
    if(month==0 || month>12) return false;
    if(day==0 || (month!=2 && day>days[month])) return false;
    if(month==2)
    {
        int reap=year%400==0 || (year%100!=0)&& year%4==0;
        if(day>days[month]+reap) return false;
    }
    
    return true;
}

int main()
{
    int day1,day2;
    scanf("%d%d",&day1,&day2);
     int res=0;
    for(int i=1000;i<10000;i++)
    {
        int day=i,x=i;
        for(int j=0;j<4;j++)
        {
            day=day*10+x%10;
            x/=10;
        }
       
        if(day1<=day && day<=day2)
        {
            if(check(day))
                res++;
        }
    }
    
    printf("%d",res);
    
    return 0;
}
5. 归并排序
题意:

给定你一个长度为n的整数数列。

请你使用归并排序对这个数列按照从小到大进行排序。

并将排好序的数列按顺序输出。

思路:

归并就是不断递归,将数据俩俩划分,通过俩俩排序最后来完成整个排序,时间复杂度nlogn。

代码:
#include <iostream>
#include <algorithm>

using namespace std;

const int N=1000100;

int a[N],temp[N];
int n;

void merge_sort(int q[],int l,int r)
{
    if(l>=r) return ;
    int k=0;
    int mid=(l+r)>>1;
    merge_sort(q,l,mid),merge_sort(q,mid+1,r);
    
    int i=l,j=mid+1;
    while(i<=mid && j<=r)
        if(q[i]<=q[j]) temp[k++]=q[i++];
        else temp[k++]=q[j++];
        
    while(i<=mid) temp[k++]=q[i++];
    while(j<=r) temp[k++]=q[j++];
    
    for(i=l,j=0;i<=r;i++,j++) q[i]=temp[j];
    
}

int main()
{
    
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }
    
    merge_sort(a,0,n-1);
    
    for(int i=0;i<n;i++) printf("%d ",a[i]);
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值