C++算法之枚举、模拟与排序

1.AcWing 1210.连号区间数

分析思路

由题意是在 1∼N 的某个排列中有多少个连号区间,所以每个数出现并且不重复!

如果是连续的,那么Max-Min==j-i([i,j])

代码实现
#include<iostream>
#include<algorithm>
using namespace std;
const int N=10010,INF = 100000000;
int a[N];
int main()
{
    int n;
    cin>>n;
    int count=0;
    for(int i=0;i<n;i++) cin>>a[i];
    for(int i=0;i<n;i++)//枚举区间的左端点
    {
        int Max=-INF,Min=INF;
        for(int j=i;j<n;j++)//枚举区间的右端点
        {
           Max=max(Max,a[j]);
           Min=min(Min,a[j]);
           if(Max-Min==j-i) count++;
        }
        
    }
    cout<<count<<endl;
    return 0;
}

2.AcWing 1236.递增三元组

分析思路

由题意可知,需要从A、B、C数组中各取一个,使得A<B<C,所以我们来枚举B数组(由于时间复杂度的限制只能枚举一个数组),这样A,C是独立的互不影响的,只与B数组的大小有关,然后从A中找到小于B的个数、从C中找到大于B的个数,两者相乘。

排序+二分:先对A、C数组进行排序然后用二分求出A中小于B的第一个个数的位置和C中大于B的第一个位置。
前缀和:

代码实现

排序+二分:

#include<iostream>
#include<algorithm>

using namespace std;
const int N=100010;
typedef long long LL;
int a[N],b[N],c[N];

int main()
{
    int n;
    cin>>n;
    for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &b[i]);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &c[i]);
    
    sort(a+1,a+n+1);
    sort(c+1,c+n+1);
    
    LL res=0;
    for(int i=1;i<=n;i++)
    {
        int l=1,r=n;
        while(l<r)
        {
            int mid=(l+r+1)/2;
            if(a[mid]<b[i]) l=mid;
            else r=mid-1;
        }
        if (a[l] < b[i]) 
        {
            int temp=l;
            l=1,r=n;
            while(l<r)
            {
                int mid=(l+r)/2;
                if(c[mid]>b[i]) r=mid;
                else l=mid+1;
            }
            if(c[r]>b[i]) res+=(LL)(temp)*(LL)(n-l+1);
        }
        
    }
    cout<<res<<endl;
    return 0;
}

前缀和:

3个数组的所有数都加1,是因为在不影响数与数之间的大小关系下,让该行代码s[b[i] - 1];

避免出现b[i]为0的情况导致空指针异常

#include<iostream>
#include<algorithm>

using namespace std;
const int N=100010;
typedef long long LL;
int a[N],b[N],c[N],s[N],t[N],x[N],y[N];

int main()
{
    int n;
    cin>>n;
    for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]),a[i]++;
    for (int i = 1; i <= n; i ++ ) scanf("%d", &b[i]),b[i]++;
    for (int i = 1; i <= n; i ++ ) scanf("%d", &c[i]),c[i]++;
    
    LL res=0;
    for(int i=1;i<=n;i++)
    {
        s[a[i]]++;
        t[c[i]]++;
    }
    for(int i=1;i<=N;i++)
    {
        x[i]=x[i-1]+s[i];
        y[i]=y[i-1]+t[i];
    }
    for(int i=1;i<=n;i++)
    {
        res+=(LL)(x[b[i]-1])*(LL)(y[N]-y[b[i]]);
    }
    
    cout<<res<<endl;
    return 0;
}

3.AcWing 1245.特别数的和

分析思路

每次取出个位数判断是否满足要求即可!

字符型数组转化成整型:

 int x=0;

for(int i=0;i<s.size();i++)

{

        x=x*10+s[i]-'0';

}

代码实现
#include<iostream>

using namespace std;
int main()
{
    int n;
    cin>>n;
    int res=0;
    for(int i=1;i<=n;i++)
    {
        int x=i;
        while(x)
        {
            int t=x%10;//取个位数
            x/=10;//去掉个位
            if(t==1||t==2||t==0||t==9) {
                res+=i;
                break;//只要存在指定数字结束本次循环
            }
        }
    }
    cout<<res<<endl;
    return 0;
}

4.AcWing 1204. 错误票据

分析思路

先对数组进行排序操作,然后遍历如果存在前一个和后一个相等则为重号ID,如果存在当a[i]>=a[i-1]+2,则两者之前存在缺失ID即a[i]-1。

代码实现
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 10010;

int n;
int a[N];

int main()
{
    int cnt;
    cin >> cnt;
    string line;

    getline(cin, line); // 忽略掉第一行的回车
    while (cnt -- )
    {
        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;
}

还可以使用以下代码对数组进行读入:

int x, i = 0;
    cin >> x;//过滤
    while(cin >> x) a[i++] = x;

5.AcWing 466.回文日期

分析思路

①枚举回文数(年份的范围是在1000~10000)

先枚举前四位,然后算出八位数的日期

int res=0;
    for(int i=1000;i<10000;i++)
    {
        int r=i,x=i;
        //形成八位数的日期
        for(int i=0;i<4;i++) r=r*10+x%10,x/=10;
        if(r>=date1&&r<=date2&&check(r)) res++;
    }

②判断是否在范围内

③判断日期是否合法

先开一个每个月份天数的集合,先判断月份(除二月之外)、天数是否满足,然后根据平年闰年对二月的天数进行讨论(平年-28天 闰年-29天)

bool check(int data)
{
    int year=data/10000;
    int month=data%10000/100;
    int day=data%100;
    
    if(!month||month>12||!day) return false;
    if(month!=2&&day>days[month]) return false;
    if(month==2)
    {
        bool flag=year%100&&year%4==0||year%400==0;
        if(day>28+flag)//如果是闰年则二月是29天需要+100
            return false;
    }
    return true;
}
代码实现
#include<iostream>
#include<algorithm>

using namespace std;
int days[13]={0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
bool check(int data)
{
    int year=data/10000;
    int month=data%10000/100;
    int day=data%100;
    
    if(!month||month>12||!day) return false;
    if(month!=2&&day>days[month]) return false;
    if(month==2)
    {
        bool flag=year%100&&year%4==0||year%400==0;
        if(day>28+flag)//如果是闰年则二月是29天需要+100
            return false;
    }
    return true;
}
int main()
{
    int date1,date2;
    cin>>date1>>date2;
    
    int res=0;
    for(int i=1000;i<10000;i++)
    {
        int r=i,x=i;
        //形成八位数的日期
        for(int i=0;i<4;i++) r=r*10+x%10,x/=10;
        if(r>=date1&&r<=date2&&check(r)) res++;
    }
    cout<<res<<endl;
    return 0;
}

6.AcWing 2867.回文日期(蓝桥杯真题)

分析思路

在上一题的基础上加上对回文日期的判断(双指针方法)AB型日期的特判

代码实现
#include<iostream>
#include<algorithm>

using namespace std;
int days[13]={0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
bool check(int data)
{
    int year=data/10000;
    int month=data%10000/100;
    int day=data%100;
    
    if(!month||month>12||!day) return false;
    if(month!=2&&day>days[month]) return false;
    if(month==2)
    {
        bool flag=year%100&&year%4==0||year%400==0;
        if(day>28+flag)//如果是闰年则二月是29天需要+100
            return false;
    }
    return true;
}
bool check1(string data)
{
    int l=data.length();
    for(int i=0,j=l-1;i<j;i++,j--)
    {
        if(data[i]!=data[j]) return false;
    }
    return true;
}
bool check2(string data)
{
    if(check1(data))
    {
        if(data[0]!=data[1]&&data[2]==data[0]&&data[3]==data[1]) return true;
        else return false;
    }
}
int main()
{
    int date1;
    int flage=0;
    cin>>date1;
    
    int res1=0,res2=0;
    for(int i=date1+1;i<=99999999;i++)
    {
        if(check(i))
        {
            string s=to_string(i);
            if(check1(s)&&!flage)//回文判断
            {
                res1=i;
                flage=1;
            } 
            if(check2(s))//回文特判
            {
                res2=i;
                break;
            }
        }
        
        
    }
    cout<<res1<<endl;
    cout<<res2<<endl;
    return 0;
}

7.AcWing 787.归并排序  

分析思路

①确定分界点 mid=(l+r)>>1

②递归排序left、right

③归并----合二为一

代码实现
#include<iostream>

using namespace std;
const int N=100010;
int q[N],temp[N];

void merge_sort(int q[],int l,int r)
{
    while(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]) temp[k++]=q[i++];
        else temp[k++]=q[j++];
    }
    
    while(i<=mid) temp[k++]=q[i++];
    while(j<=r) temp[k++]=q[j++];
    
    for(int i=l,j=0;i<=r;i++,j++) q[i]=temp[j];
}

int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++) cin>>q[i];
    
    merge_sort(q,0,n-1);
    
    for(int i=0;i<n;i++) cout<<q[i]<<" ";
    
    return 0;
}

8.AcWing 788.逆序对的数量

分析思路

在归并排序的归并这一步骤中添加一个计数的操作,res+=mid-i+1在左半段有大于右半段这种情况下满足逆序对存在的条件,则左半段后面部分也同样大于右半段此时的值。

while(i<=mid&&j<=r)
    {
        if(q[i]<=q[j]) temp[k++]=q[i++];
        else 
        {
            temp[k++]=q[j++];
            res+=mid-i+1;
        }
    }
代码实现
#include<iostream>

using namespace std;
const int N=100010;
int q[N],temp[N];
long long res;

void merge_sort(int q[],int l,int r)
{
    while(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]) temp[k++]=q[i++];
        else 
        {
            temp[k++]=q[j++];
            res+=mid-i+1;
        }
    }
    
    while(i<=mid) temp[k++]=q[i++];
    while(j<=r) temp[k++]=q[j++];
    
    for(int i=l,j=0;i<=r;i++,j++) q[i]=temp[j];
}

int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++) cin>>q[i];
    
    merge_sort(q,0,n-1);
    
    cout<<res<<endl;
    
    return 0;
}

9.AcWing 1219.移动距离  

分析思路

由二维数组的值来计算该数所在的行列数,这里便于好运算我们从0开始(所有数都减一),w为一行多少个数,x为该数的值,行号:x/w 列号:x%w(奇数行需要反转,w-1-x%w)

 曼哈顿距离:\left | x1-x2 \right |+\left | y1-y2 \right |

欧几里得距离:\sqrt{(x1-x2)^{2}+(y1-y2)^{2}}

代码实现
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

int main()
{
    int w, m, n;
    cin >> w >> m >> n;
    m --, n -- ;

    int x1 = m / w, x2 = n / w;
    int y1 = m % w, y2 = n % w;
    if (x1 % 2) y1 = w - 1 - y1;
    if (x2 % 2) y2 = w - 1 - y2;

    cout << abs(x1 - x2) + abs(y1 - y2) << endl;

    return 0;
}

10.AcWing 1229. 日期问题

分析思路

我们将日期看作八位数的数字,从19600101到20591231枚举,然后检验一下日期是否合法,再分三种情况输出日期的组成(年/月/日、月/日/年、日/月/年)

这里最好用scanf和printf进行格式化输入输出

 scanf("%d/%d/%d",&a,&b,&c);

printf("%d-%02d-%02d\n", year, month, day);(02代表不足两位用0补齐)

代码实现
#include<iostream>
#include<cstdio>

using namespace std;
int days[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
bool check(int year,int month,int day)
{
    if(month==0||month>12) return false;
    if(day==0) return false;
    if(month!=2)
    {
        if(day>days[month]) return false;
    }
    if(month==2)
    {
        int leap=year%100&&year%4==0||year%400==0;
        if(day>28+leap) return false;
    }
    return true;
}
int main()
{
    int a,b,c;
    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(year,month,day))
        {
            //三种表示情况
            if (year % 100 == a && month == b && day == c ||        // 年/月/日
                month == a && day == b && year % 100 == c ||        // 月/日/年
                day == a && month == b &&year % 100 == c)           // 日/月/年
                printf("%d-%02d-%02d\n", year, month, day);
        }
    }
    return 0;
}

11.AcWing 1231.航班时间

分析思路

①分析题意:

                往东飞 : 到达时间 - (起飞时间 - 时差)= 到达时间1 - 起飞时间1 + 时差
                往西飞: 到达时间 - (起飞时间 + 时差)= 到达时间2 - 起飞时间2 - 时差
                两次时间相加 = 到达时间1 - 起飞时间1 + 到达时间2 - 起飞时间2
                所以一次飞行时间为: 两次的时间差 / 2

②具体思路:

我们将时间化成秒来计算,使用getline读取一行,然后判断后面是否有(+1)这种情况,统一格式如果没有加"(+0)",再用sscanf(a,b,c)

line.c_str():把string 对象转换成c中的字符串样式

line.back():返回字符串最后一个字符

sscanf(a,b,c):是把a中的每个字符,按照b格式,赋值给c

代码实现
#include<iostream>
#include<cstring>
#include<cstdio>
#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)+24*60*60*d;
    
}
int main()
{
    int n;
    scanf("%d\n",&n);//忽略掉第一行的回车

    while(n--)
    {
        int time=(get_time()+get_time())/2;
        int hour=time/3600,minute=time%3600/60,second=time%60;
        printf("%02d:%02d:%02d\n",hour,minute,second);
    }
    return 0;
}

12.AcWing 1241.外卖店优先级、

分析思路

因为店铺订单呈离散化,所以我们压缩,直接算这次订单和上次订单两者之前相差的时间。

优秀题解:AcWing 1241. 外卖店优先级 - AcWing 

代码实现
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 100010;

int n, m, T;
//分别表示第i个店铺当前的优先级、表示第i个店铺上一次有订单的时刻
int score[N], last[N];
//表示第i个店铺当前是否处于优先缓存中
bool st[N];

PII order[N];
int main()
{
    scanf("%d%d%d",&n,&m,&T);
    for(int i=0;i<m;i++) scanf("%d%d",&order[i].x,&order[i].y);
    sort(order,order+m);//pair排序先对比first,如果一样再看second
    
    for(int i=0;i<m;)
    {
        //处理同一时间有多个订单
        int j=i;
        while(j<m&&order[j]==order[i]) j++;
        int t=order[i].x,id=order[i].y,cnt=j-i;
        i=j;//然后便从j开始
        
        //处理的是t时刻之前的信息
        score[id]-=t-last[id]-1;
        if(score[id]<0) score[id]=0;
        if(score[id]<=3) st[id]=false;
        
        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)
        {
            score[i]-=T-last[i];//因为不包含T
            if(score[i]<=3) st[i]=false;
        }
    int res=0;
    for(int i=1;i<=n;i++) res+=st[i];
    
    printf("%d\n",res);
}

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值