原题链接:计算机软件能力认证考试系统
这道题看起来很复杂,但给的时间是10.0s,足够我们去暴力遍历了。
思路1:依次处理每一个任务,将该任务的minutes,hours,day,month,week记录下来,采用5个vector<pair<int,int>>,以hour为例,pair<int,int>表示该任务能执行的hour时间段,存储其能执行的所有hour时间段。时间从开始时间遍历到结束的时间,判断当前时间是否在该任务的执行时间中,是则存储到优先队列中,然后再去处理下一个任务。最后优先队列已经排好序了,输出所有符合的任务。
改编自:201712-3 CCF Crontab 满分题解(详细代码注释) + 解题思路_一只可爱的小猴子的博客-优快云博客
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
typedef long long LL;
using namespace std;
//处理英文缩写
map<string,int> dic={{"jan",1},{"feb",2},{"mar",3},{"apr",4},{"may",5},{"jun",6},
{"jul",7},{"aug",8},{"sep",9},{"oct",10},{"nov",11},{"dec",12},
{"sun",0},{"mon",1},{"tue",2},{"wed",3},{"thu",4},{"fri",5},{"sat",6}};
//平年的每月天数
int day[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
struct CMD{//任务结构体,其中时间为yyyymmddhh(mi)(mi),即年月日时分连在一起的一长串数字
int id;
LL time;
string cmd;
bool operator <(const CMD &a) const{
return (time==a.time)?id>a.id:time>a.time;
}
};
priority_queue<CMD> q;//优先队列,时间短的在前,时间相同则编号小的在前
int sy,sm,sd,sh,smi,ey,em,ed,eh,emi;//开始时间和结束时间
LL endtime;//结束时间的yyyymmddhh(mi)(mi)形式
vector<pair<int,int>> v[5];//分别记录一条命令的mi,h,d,m,w,pair<int,int>表示一个区间,如果只有一个数,如1,则记(1,1),如果是*,则记(-1,-1);
int leapyear(int y,int m){//如果是闰年2月,天数比平年的day[2]多1,其他相同
if(m==2) return (y%400==0||(y%100!=0&&y%4==0))?1:0;
return 0;
}
int weekday(int y,int m,int d){//蔡勒公式求星期几
if(m==1||m==2) m+=12,y--;
int c=y/100;
y%=100;
int w=c/4-2*c+y/4+y+26*(m+1)/10+d-1;
return (w%7>=0)?(w%7):(w%7+7);
}
bool judge(int m,int id){//判断该任务的第id个属性是否包含m
for(int i=0;i<(int)v[id].size();i++)//只要有一个区间包括m就符合要求
if(v[id][i].first==-1||(v[id][i].first<=m&&m<=v[id][i].second)) return true;
return false;
}
bool end_time_check(int y,int m,int d,int h,int mi){//判断是否超出了endtime
LL nowtime=(LL)y*100000000+(LL)m*1000000+(LL)d*10000+(LL)h*100+mi;
return nowtime<endtime;
}
int getval(string str){//获取内容
if(isdigit(str[0])) return stoi(str);//数字形式
transform(str.begin(),str.end(),str.begin(),::tolower);//转化为小写
return dic[str];//获取英文缩写对应的内容
}
void setsubval(string str,int id){//处理已被逗号分隔开的string
int p=str.find('-'),val1,val2;//判断是否有'-'
if(p==-1) val1=val2=getval(str);//无'-’则左右端点相同
else{//有'-'则分别获取左右端点
val1=getval(str.substr(0,p));
val2=getval(str.substr(p+1));
}
v[id].push_back(make_pair(val1,val2));//将此区间加入到该任务的第id个属性
}
void setval(string str,int id){//设置该任务的第id个属性
if(str=="*") v[id].push_back(make_pair(-1,-1));//'*'不会与其他字符同时出现,记录完退出
else{
int p=str.find(',');//按逗号分隔开
while(p!=-1){
setsubval(str.substr(0,p),id);//一节一节处理
str=str.substr(p+1);
p=str.find(',');
}
setsubval(str,id);//最后一节也要处理
}
}
int main()
{
int n;
scanf("%d %04d%02d%02d%02d%02d %04d%02d%02d%02d%02d",&n,&sy,&sm,&sd,&sh,&smi,&ey,&em,&ed,&eh,&emi);
endtime=(LL)ey*100000000+(LL)em*1000000+(LL)ed*10000+(LL)eh*100+emi;//计算endtime
for(int i=0;i<n;i++){//依此处理任务
string ss,cmd;
for(int j=0;j<5;j++){//处理该任务的5个属性
v[j].clear();//清空上一个任务的属性
cin>>ss;
setval(ss,j);
}
cin>>cmd;
int ny=sy,nm=sm,nd=sd,nh=sh,nmi=smi;
for(;ny<=ey;ny++,nm=1)
for(;nm<=12;nm++,nd=1)
if(judge(nm,3))//判断月份
for(;nd<=day[nm]+leapyear(ny,nm);nd++,nh=0)
if(judge(nd,2)&&judge(weekday(ny,nm,nd),4))//判断日期和星期
for(;nh<24;nh++,nmi=0)
if(judge(nh,1))//判断小时
for(;nmi<60;nmi++){
if(!end_time_check(ny,nm,nd,nh,nmi)) break;//不能超出endtime
if(judge(nmi,0)){//在规定时间内
CMD c;
c.id=i;
c.time=(LL)ny*100000000+(LL)nm*1000000+(LL)nd*10000+(LL)nh*100+nmi;
c.cmd=cmd;
q.push(c);//放入优先队列
}
}
}
while(!q.empty()){
CMD c=q.top();
q.pop();
cout<<c.time<<" "<<c.cmd<<endl;
}
return 0;
}
思路2:提前处理好所有任务可执行的时间,在线输出。从开始时间遍历到最后时间,每一个时间点遍历所有的任务,如果符合,则输出。
改编自:CCF201712-3 Crontab(100分)【模拟+文本处理】_海岛Blog的博客-优快云博客
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
typedef long long LL;
using namespace std;
//处理英文缩写
map<string,int> dic={{"jan",1},{"feb",2},{"mar",3},{"apr",4},{"may",5},{"jun",6},
{"jul",7},{"aug",8},{"sep",9},{"oct",10},{"nov",11},{"dec",12},
{"sun",0},{"mon",1},{"tue",2},{"wed",3},{"thu",4},{"fri",5},{"sat",6}};
//平年的每月天数
int day[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int get(string str){//获取内容
if(isdigit(str[0])) return stoi(str);//数字形式
transform(str.begin(),str.end(),str.begin(),::tolower);//转化为小写
return dic[str];//返回英文缩写对应的内容
}
vector<int> workstr(string str,int len){
vector<int> v(len);//初始化
if(str=="*")//'*'表示所有时间点都可以,都标记为1
for(int i=0;i<len;i++) v[i]=1;
else{
vector<string> val;//存储每一个时间段
int p=str.find(',');//按逗号分隔开
while(p!=-1){
val.push_back(str.substr(0,p));
str=str.substr(p+1);
p=str.find(',');
}
val.push_back(str);//把最后一个时间段也记录下来
for(auto s:val){//处理每一个时间段
p=s.find('-');
if(p==-1) v[get(s)]=1;//只有一个数字
else{//是一个区间
int l=get(s.substr(0,p)),r=get(s.substr(p+1));
for(int i=l;i<=r;i++) v[i]=1;//将此区间内的所有时间点标记为1
}
}
}
return v;
}
struct CMD{//任务结构体
vector<int> v[5];
string cmd;
CMD(){}
CMD(string s0,string s1,string s2,string s3,string s4,string c){
v[0]=workstr(s0,60);//分钟[0,59],1为可执行时间,0为不可执行时间
v[1]=workstr(s1,24);//小时[0,23]
v[2]=workstr(s2,32);//天[1,31]
v[3]=workstr(s3,13);//月[1,12]
v[4]=workstr(s4,7);//星期[0,6]
cmd=c;
}
}C[25];
int leapyear(int y,int m){//如果是闰年2月,天数比平年的day[2]多1,其他相同
if(m==2) return (y%400==0||(y%100!=0&&y%4==0))?1:0;
return 0;
}
int weekday(int y,int m,int d){//蔡勒公式求星期几
if(m==1||m==2) m+=12,y--;
int c=y/100;
y%=100;
int w=c/4-2*c+y/4+y+26*(m+1)/10+d-1;
return (w%7>=0)?(w%7):(w%7+7);
}
struct time{//时间结构体
int y,m,d,h,mi,w;
void add(){//加一分钟后的时间
mi++;
if(mi==60){
mi=0; h++;
if(h==24){
h=0; d++; w=(w+1)%7;
if(d>day[m]+leapyear(y,m)){
d=1; m++;
if(m==13){
m=1; y++;
}
}
}
}
}
bool operator < (const time &t) const{//比较
if(y!=t.y) return y<t.y;
if(m!=t.m) return m<t.m;
if(d!=t.d) return d<t.d;
if(h!=t.h) return h<t.h;
return mi<t.mi;
}
};
int main()
{
int n;
time s,e;
scanf("%d %04d%02d%02d%02d%02d %04d%02d%02d%02d%02d",&n,&s.y,&s.m,&s.d,&s.h,&s.mi,&e.y,&e.m,&e.d,&e.h,&e.mi);
s.w=weekday(s.y,s.m,s.d); e.w=weekday(e.y,e.m,e.d);//两个时间
for(int i=0;i<n;i++){
string s0,s1,s2,s3,s4,c;
cin>>s0>>s1>>s2>>s3>>s4>>c;
C[i]=CMD(s0,s1,s2,s3,s4,c);//每一个任务
}
for( auto i=s;i<e;i.add()){//从开始时间遍历到最后时间
for(int j=0;j<n;j++){//遍历每一个任务
//如果当前任务符合时间,输出
if(C[j].v[0][i.mi]&&C[j].v[1][i.h]&&C[j].v[2][i.d]&&C[j].v[3][i.m]&&C[j].v[4][i.w])
printf("%04d%02d%02d%02d%02d ",i.y,i.m,i.d,i.h,i.mi),cout<<C[j].cmd<<endl;
}
}
return 0;
}