枚举&模拟

枚举&模拟

模拟

1210. 连号区间数

二重循环枚举所有子区间,然后sort,然后判断是否符合条件(满足max-min=b-a),这个判断条件是要想清楚的

#include
#include

using namespace std;

const int N =10020,INF=100000000;
int n;
int a[N];

int main()
{
     
    cin>>n;

    for(int i=0;i<n;i++)  cin>>a[i];
     
    int res=0;
    for(int i=0;i<n;i++)//枚举所有子区间,左端点
    {
     
        int maxv=-INF,minv=INF;//找每个区间的最大值最小值
        for(int j=i;j<n;j++)//右端点
        {
     
            maxv=max(maxv,a[j]);
            minv=min(minv,a[j]);
            
            if(maxv-minv==j-i) res++;
        }
    }
    
    cout<<res<<endl;
    return 0;
}

1236. 递增三元组

1.暴力枚举三元组,判断是否满足条件,cnt++

2.枚举中间的B,找A中比bi小的个数,C中比bi大的个数,相乘=中间为bi时的方案数

如何找A中比bi小的…枚举?…用前缀和

(1)cnt[i]表示在A中,i出现的次数; 一个for循环遍历A,cnt[A[i]]++;

(2)用cnt构造前缀和,s[i]=cnt[0]+cnt[1]+…cnt[i],表示在A中0~i出现的次数,s[b[i-1]],即在A中多少个数字小于bi

#include
#include
#include
#include
using namespace std;

typedef long long LL;

const int N=100010;

int n;
int a[N],b[N],c[N];
int as[N];//表示在A[]中有多少个数小于b[i]
int cs[N];//表示在C[]中有多少个数小于b[i]
int cnt[N],s[N];
int main()
{
     
    scanf("%d",&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]);//变为从1开始
    
    //求as[]
    for(int i=0;i<n;i++) cnt[a[i]]++;//每一个a出现多少次
    for(int i=1;i<N;i++) s[i]=s[i-1]+cnt[i];//求cnt前缀和
    for(int i=0; i<n;i++) as[i]=s[b[i]];
    
    memset(cnt,0,sizeof cnt);//清空cnt
    memset(s,0,sizeof s);
    for(int i=0;i<n;i++) cnt[c[i]]++;
    for(int i=1;i<N;i++) s[i]=s[i-1]+cnt[i];//求cnt前缀和
    for(int i=0; i<n;i++) cs[i]=s[N-1]-s[b[i]-1];
    
    //枚举每个b[i]
    LL res=0;
    for(int i=0;i<n;i++) res+=(LL)as[i]*cs[i];
    cout<<res<<endl;
    return 0;
}

1245.特别数的和

就是一个数中只要含有2,0,1,9其中一个及以上,就符合条件,所以考虑怎么把数的每一位分离

1.把每一位数抠出来

int x=i;

while(x)

{

​ int t=x%10;

​ x/=10;

​ if( t == 2 || t == 0|| t==1 || t=9) res+=i;

}

2.把“2019”变成 2019

int x=0;

for(int i=0;i

#include
using namespace std;
int n;

int main()
{
     
    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==2||t==0||t==1||t==9)
            {
     
                res+=i;
                break;
            }
        }
    }
    cout<<res<<endl;
    return 0;
}

1204. 错误票据

找断号、重号ID,首先想到是排序+遍历

1.此题输入无规律(ID个数不固定),故用字符串读

2.如何快速找到重号,断号ID

​ 2.1将a[n]排序

​ 2.2遍历a[n],若a[i]==a[i-1],发现重号ID

​ 2.3若a[i]>=a[i-1]+2,发现断号ID

#include
#include
#include
#include
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);//从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]) res1=a[i];
        else if(a[i]>=a[i-1]+2) res2=a[i]-1;
    }
    
    cout<<res2<<' '<<res1<<endl;
    return 0;
}

给定两个日期,求之间有多少是回文日期

1.枚举日期+判断是否回文,相当于手写日历

2.枚举回文串+判断是否合法日期

​ 2.1枚举回文串(只枚举年份即可如2011)

​ 2.2判断是否在所给日期范围内(直接当数值)

​ 2.3判断日期是否合法

​ 2.3.1 构造回文日期(比如枚举到2011----> 2011.11.02)

for(int j=0;j<4;j++) date=date*10+x%10,x/=10;//每次取最后一个数字加到后面

​ 2.3.2 分离年月日

int year=date/10000;
int month=date%10000/100;
int day=date%100;

​ 2.3.3 判断月份是否不对

​ 2.3.4 判断月份对应的天数是否不对(非2月的情况下)

天数事先存好,只要索引即可 days[13]={0,31,28,31,30,31,30,31,31,30,31,30,31}

​ 2.3.5 在2月的情况下判断是否闰年+判断天数

if(month==2)
{
int leap=year%100 && year%4 == 0 || year%400 ==0;
if(day>28 +leap ) return false;
}

​ 2.3.6返回判断结果

#include
#include
#include

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

1229. 日期问题

1.枚举日期

2.判断是否合法

3.判断是否是可能的表示

#include
#include

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||
            year%100==c && month==a && day==b||
            year%100==c && month==b && day==a)
                printf("%d-%02d-%02d\n",year,month,day);//不足两位补0
        }
    }
    
    return 0;
}

787. 归并排序

模板

1.确定分界点

2.递归地将左右排序

3.合二为一(归并)

​ 3.1归并时用的是双指针思想

​ 3.1.1有两个指针分别指向左右序列的头

​ 3.1.2小的那个放到临时新序列,指针移动,大的不移动

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

​ 3.1.3直到有一个指针移到末尾了,将剩下数据全部填过来

while(i<=mid) tmp[k++]=q[i++];
while(j<=r) tmp[k++]=q[j++];

#include
using namespace std;
const int N =1000010;
int n;
int q[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(int i=l,j=0;i<=r;i++,j++) q[i]=tmp[j];
}
int main()
{
     
    scanf("%d",&n);
    for(int i=0;i<n;i++) scanf("%d",&q[i]);
    
    merge_sort(q,0,n-1);
    
    for(int i=0;i<n;i++) printf("%d ",q[i]);
    
    return 0;
}

1219. 移动距离

蛇形矩阵,求任意两点曼哈顿距离|x1-x2|+|y1-y2|

1 2 3 4 5 6

12 11 10 9 8 7

13 14 15 16 17 18

关键,给出一个点的数值,得到它的坐标

行号=n/宽度 下取整

非蛇形矩阵 列号=n%宽度

列号=奇数行(即n/宽度为奇数)要反转

#include
#include
#include
using namespace std;


int main()
{
     
    int w,m,n;
    scanf("%d%d%d",&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;
}

1241. 外卖店优先级

如何模拟这个过程?

1.暴力:枚举每个时刻的所有情况

​ 1.1判断哪些店有订单,哪些无订单

​ 1.2有订单的店score+=2*订单

​ 1.3无订单score–(若优先级为0,则不变)

​ 1.4若score>5 st[id]=true; 若score<=3 st[id]=false//移出优先缓存

​ 1.5遍历所有店铺 res+=st[i],留下的都是此时刻内在优先缓存的

2.将无订单的店的score–,先不处理,直到下次它有订单的时候一起处理

​ 2.0 score[i]表示第i个店铺当前优先级;last[i]表示第i个店铺上一次有订单的时刻;st[i]表示第i个店铺当前是否处于优先缓存

​ 2.1将所有订单按照时间排序

​ 2.2对于每个订单,每次处理一批订单

​ 2.2.1 score[id]-=t-last[id]-1;

你可能感兴趣的:(笔记,算法)