专题三——枚举、模拟、排序

1.1236. 递增三元组

方法一:二分要在ac两个数组中找严格小于或大于b[i]的第一个位置【经过我与大佬的苦苦钻研才改好/(ㄒoㄒ)/~~】

#include <bits/stdc++.h>

using namespace std;
typedef long long ll; 
const int N=100010;
ll a[N],b[N],c[N];
ll ax[N],cx[N];
ll cnt[N],s[N];
ll n;
ll finda(ll a[],ll u)
{
    ll l=1,r=n;
    ll cnt = 0;
    while(l<r)
    {
        ll mid=l+r+1>>1;
        if(a[mid] < u) l=mid;
        else r=mid-1;
    }
    if(a[l]>=u) return 0;
    else return l;
}
ll findc(ll c[],ll u)
{
    ll l=1,r=n;
    while(l<r)
    {
        ll mid=l+r>>1;
        if(c[mid] > u) r=mid;
        else l=mid + 1;
    }
   if(c[r] <= u) return 0;
   else return n - r + 1;
  
}

int main()
{
    cin>>n;
    for(ll i=1;i<=n;i++) scanf("%lld",&a[i]);
    for(ll i=1;i<=n;i++) scanf("%lld",&b[i]);
    for(ll i=1;i<=n;i++) scanf("%lld",&c[i]);
    sort(a+1,a+n+1);sort(c+1,c+n+1);
   // for(int i=1;i<=n;i++) cout<<c[i]<<endl;
    ll res=0;
    for(ll i=1;i<=n;i++)
    {
        ax[i]=finda(a,b[i]);
        //cout<<ax[i]<<endl;
        cx[i]=findc(c,b[i]);
        //cout<<cx[i]<<endl;
        res+=ax[i]*cx[i];
    }
   cout<<res<<endl;
    return 0;
}

方法二:前缀和:记录前面小于b[i]的个数一共有多少,与题意恰好吻合【妙啊】

#include <bits/stdc++.h>

using namespace std;
const int N=100010;
int a[N],b[N],c[N];
int ax[N],cx[N];
int cnt[N],s[N];
int n;
int main()
{
    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]++;
    for(int i=1;i<=n;i++) cnt[a[i]]++;
    for(int i=1;i<N;i++) s[i]=s[i-1]+cnt[i];
    for(int i=1;i<=n;i++) ax[i]=s[b[i]-1];
    memset(cnt,0,sizeof cnt);
    memset(s,0,sizeof s);
    for(int i=1;i<=n;i++) cnt[c[i]]++;
    for(int i=1;i<N;i++) s[i]=s[i-1]+cnt[i];
    for(int i=1;i<=n;i++) cx[i]=s[N-1]-s[b[i]];
    long long res=0;
    for(int i=1;i<=n;i++)
    {
        res+=(long long)ax[i]*cx[i];
    }
   cout<<res<<endl;
    return 0;
}

2.1210. 连号区间数

连号区间的性质:区间的max-min==下标之差!

#include <bits/stdc++.h>

using namespace std;
const int N=10005,INF=1e9;
int a[N];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    int res=0;
    for(int i=1;i<=n;i++)
    {
        int maxx=-INF,minx=INF;
        for(int j=i;j<=n;j++)
        {
            maxx=max(maxx,a[j]);
            minx=min(minx,a[j]);
            if(maxx-minx==j-i) res++;
        }
    }
    cout<<res<<endl;
    return 0;
}

3.1204错误票据

乍一看:这水题\偷笑;仔细想:map?桶排?臣妾做不到啊/(ㄒoㄒ)/~~
值得学习的地方是对输入的处理:
(1)getline
(2)stringstream

相关资料链接:

#include <bits/stdc++.h>

using namespace std;
const int N=1e5+5;
int n;
int a[N];
int main()
{
    int cnt;
    cin>>cnt;
    string line;
    getline(cin,line);//吃回车
    while(cnt--)
    {
        getline(cin,line);//接收空格读入一个流
        stringstream ssin(line);//ssin代替cin接管line流里面的数据
        while(ssin>>a[n]) n++;//从ssin找出a[n],并完成数据类型的自动转化
    }
    sort(a,a+n);
    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;
    printf("%d %d\n",res1,res2);
    return 0;
}

4.466. 回文日期

如果按照“常规思路”写会超时,所以本题采取的策略是:
①先枚举回文数(而且只枚举前四位,后四位直接翻过去)
②判断当前所枚举的日期是否在范围内
③最后判断日期是否合法:年月日分别取出来判断,最后考虑二月
tip:(1)闰年:leap=year%4== 0&&year%100!=0||year%400==0;
(2)优先级:!>&&>||

#include <bits/stdc++.h>

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

bool check_valid(int date)
{
    int year=date/10000;
    int month =date%10000/100;
    int day=date%100;
    if(month==0||month>12) return false;
    if(day==0||month!=2&&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 date1,date2;
    cin>>date1>>date2;
    int res=0;
    for(int i=1000;i<10000;i++)
    {
        int date=i,x=i;
        //创造回文串
        for(int j=0;j<4;j++) {date=date*10+x%10;x/=10;}
        if(date1<=date&&date<=date2&&check_valid(date)) res++;
    }
    cout<<res<<endl;
    return 0;
}

5.1219. 移动距离

模拟题:让我情不自禁想到蛇形矩阵(以及被其支配的恐惧),然后惊奇地发现贪吃蛇解法被我忘记了(⊙﹏⊙)(还不去复习)

tips:

①两种距离:
曼哈顿距离:d1=|x1-x2|+|y1-y2|;【本题】
欧几里得距离:d2=sqrt( ( x 1 − x 2 ) 2 (x1-x2)^2 (x1x2)2+ ( y 1 − y 2 ) 2 (y1-y2)^2 (y1y2)2)
②所有数字在输入时的编号-1
③行号:n/w,m/w ; 列号:n%w,m%w,奇数行就翻转

#include <bits/stdc++.h>

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;
}

6.1229.日期问题

手写版,好麻烦啊好麻烦,排序,输出,输入搞得我头大;数据太水了,被我混过去了

#include <bits/stdc++.h>

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

bool check_valid(int year,int month,int day)
{
    if(year>2059||year<1960)
        return false;
    if(month==0||month>12)
        return false;
    if(day==0||month!=2&&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;
}
void nian(int a,int tou,int b,int c)
{
    a+=tou*100;
    if(check_valid(a,b,c))
    {
        if(b>=1&&b<=9&&c>=1&&c<=9)
            printf("%d-0%d-0%d\n",a,b,c);
        else if(b>=1&&b<=9&&c>9)
            printf("%d-0%d-%d\n",a,b,c);
        else if(b>9&&c>=1&&c<=9)
            printf("%d-%d-0%d\n",a,b,c);
        else
            printf("%d-%d-%d\n",a,b,c);
    }
}
int main()
{
    int a,b,c;
    char st;
    cin>>a>>st>>b>>st>>c;
    //sort(a,a+3);
    if(a<=b&&b<=c)
    {
        if(a==b&&b==c)
        {
            nian(a,19,b,c);
            nian(a,20,b,c);
            return 0;
        }
        nian(a,19,b,c);
        nian(c,19,a,b);
        nian(c,19,b,a);
        nian(a,20,b,c);
        nian(c,20,a,b);
        nian(c,20,b,a);
    }
   else if(a<c&&c<=b)
    {
        nian(a,19,b,c);
        nian(c,19,a,b);
        nian(c,19,b,a);
        nian(a,20,b,c);
        nian(c,20,a,b);
        nian(c,20,b,a);
    }
    else if(b<=a&&a<c)
    {
        nian(a,19,b,c);
        nian(c,19,b,a);
        nian(c,19,a,b);
        nian(a,20,b,c);
        nian(c,20,b,a);
        nian(c,20,a,b);
    }
   else if(c<=a&&a<b)
    {
        nian(c,19,a,b);
        nian(c,19,b,a);
        nian(a,19,b,c);
        nian(c,20,a,b);
        nian(c,20,b,a);
        nian(a,20,b,c);
    }
  else if(b<c&&c<a)
    {
        nian(c,19,b,a);
        nian(c,19,a,b);
        nian(a,19,b,c);
        nian(c,20,b,a);
        nian(c,20,a,b);
        nian(a,20,b,c);
    }
    else if(c<b&&b<=a)
    {
        if(a==b)
        {
            nian(c,19,b,a);
            nian(a,19,b,c);
            nian(c,20,b,a);
            nian(a,20,b,c);
            return 0;
        }
        nian(c,19,b,a);
        nian(c,19,a,b);
        nian(a,19,b,c);
        nian(c,20,b,a);
        nian(c,20,a,b);
        nian(a,20,b,c);
    }
    return 0;
}

y总版:按顺序枚举,没有了顺序的烦恼【妙啊】

#include <bits/stdc++.h>

using namespace std;
int days[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
bool check_valid(int year,int month,int day)
{
    if(month==0||month>12)
        return false;
    if(day==0||month!=2&&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 date=19600101;date<=20591231;date++)
    {
        int year =date/10000,month=date%10000/100,day=date%100;
        if(check_valid(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);//不足两位自动补0
        }
    }
    return 0;
}

7.1231. 航班时间

时差在往返飞的过程中被消掉了,最后推出的公式为两个相差时间的和/2,输入的处理十分新鲜(也没多新鲜,和昨天的差不多)
tips:①sscanf :从流中读入数据 ②…c_str():将string 类型转化成char[N]

#include <bits/stdc++.h>

using namespace std;
int get_second(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_second(h2,m2,s2)-get_second(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\n",hour,minute,second);
    }
    return 0;
}

8.1241外卖店优先级

普通模拟会tle,所以有了批量处理两个订单区间

#include <bits/stdc++.h>

using namespace std;
const int N=1e5+10;
typedef pair<int,int >PII;
int n,m,T;
int score[N],last[N];
bool st[N];
PII order[N];
int main()
{

    cin>>n>>m>>T;
    for(int i=0; i<m; i++)
        scanf("%d%d",&order[i].first,&order[i].second);
    sort(order,order+m);//pair自带的排序

    for(int i=0; i<m;)
    {
        //处理同一时刻同一店铺的订单数量
        int j=i;
        while(j<m&&order[j]==order[i])
            j++;
        int t=order[i].first,id=order[i].second,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;
        score[id]+=cnt*2;
        if(score[id]>5)
            st[id]=true;
        last[id]=t;
    }
    //处理最后订单时刻和t的关系
    for(int i=1; i<=n; i++)
        if(last[i]<T)
        {
            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];
    printf("%d\n",res);
    return 0;
}

9.1238. 日志统计

计算区间同样需要优化处理【与上题对比】

#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef pair<int ,int> PII;
const int N=100010;
int n,d,k;
PII logs[N];
int cnt[N];
bool st[N];
int main()
{
    scanf("%d%d%d",&n,&d,&k);
    for(int i=0;i<n;i++) scanf("%d%d",&logs[i].x,&logs[i].y);
    sort(logs,logs+n);
    for(int i=0,j=0;i<n;i++)
    {
        int id=logs[i].y;
        cnt[id]++;
        while(logs[i].x-logs[j].x>=d)
        {
            cnt[logs[j].y]--;//计算下一个区间
            j++;
        }
        if(cnt[id]>=k) st[id]=true;
    }
    for(int i=0;i<=100000;i++)
        if(st[i]) printf("%d\n",i);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值