【CSP认证】201712_03 Crontab

参考文章&解题思路

收获
CSP中不允许STL容器嵌套,否则会导致编译错误。(来源)
比如如下代码,提交之后就会报错:

map<string,list<string>> user;

但是如果map声明如下,则不会报错:

map<string,list<string> > user;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

#include<iostream>
#include<cstring>
#include<vector>
#include<set>
#include<map>
//#include<cctype> // 可以不加 
#include<algorithm>
typedef long long LL;
using namespace std;

int n;
int firstYear,lastYear; // 输入数据的开始与结束年份 
int range[5] = {60,24,31,12,7}; // 当为*时,5个配置项对应可选的范围
int days[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31}; // 平年
string week[] = {"sun","mon","tue","wed","thu","fri","sat"};
string mon[] = {"jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"};
map<string,int> mp; // 保存各个英文缩写对应的值(方便根据字符串找到其int形式,目的是为了最终得到像题目一样的长数字表示) 
vector<pair<LL,int> > ans; // ans存放结果,"时间-操作id"对

// 返回该字符串或字符对应的int数字 
int s2i(string s)
{
	if(isalpha(s[0])) return mp[s]; // 英文单词,直接通过map映射存入对应的数字 
	// 数字,转换为int形式返回 
	int res=0;
	for(int i=0;i<s.size();i++)
		res = res*10+(s[i]-'0');
	return res;		
} 

// 处理该分离项表示的范围 
void dealMinus(set<LL> &valid,string str)
{
	for(int i=0;i<str.size();i++) // 将字符串统一转化为小写,与我们设置的预处理统一形式 
		str[i] = tolower(str[i]); 
	size_t tp = str.find('-'); // 找到'-',以获取两边的字符
	if(tp==string::npos) // 是单个字符或单个英文单词 
		valid.insert(s2i(str));  // 转化为对应的数字后直接存入
	else{
		string left = str.substr(0,tp);
		string right = str.substr(tp+1); // 从下标tp+1直接截取至串尾
		int l = s2i(left),r = s2i(right); //左右两个的数字
		for(int i=l;i<=r;i++) // 将这个范围内的数字全部存入 
			valid.insert(i);
	} 
}

// 分析当前配置项的string:先判断"*",再判断",",最后"-" 
void findValue(set<LL> &valid,string s,int j)
{
	// 配置项为*,*单独出现
	if(s=="*"){  
		for(int i=0;i<range[j];i++){ // range[j]为配置项j的可选范围 
			if(j==2 || j==3) // 由题知,日期days和月份mon从1开始 
				valid.insert(i+1); 
			else
				valid.insert(i);
		} 
		return; // 处理完*,直接返回 
	}
	// 通过 , 来单独分离出该配置项所有满足的值 
	s += ','; 
	string str; // 保存每个分离出的字符串片段 如"1,3-5"可分离出"1"和"3-5" 
	size_t pos=0,next=s.find(','); // size_t相当于unsigned int,适合表示下标
	while(next!=string::npos){ // 所有都查询完后即退出 
		str = s.substr(pos,next-pos); // 起点,长度
		dealMinus(valid,str);
		pos = next+1; // 起点指向','的下一个元素下标
		next = s.find(',',pos); // 以pos为起点,查找下一个','的下标 	
	} 
} 

// 判断这一天是星期几(该日期不合法是返回-1) 
int getDay(int y,int m,int d)
{
	days[2] = 28; // 默认是平年2月为28天 
	if(y%4==0 && y%100!=0 || y%400==0)
		days[2] = 29; // 闰年时2月有29天 
	if(d>days[m]) return -1; // 该日期不存在
	
	// 求星期几 以 1970年1月1日 是星期四来判断 
	int sum = 4;
	int q = y-1970;
	sum += q/4*5+q%4; // 每隔4年,sum+5
	if(q%4==3) ++sum; // 这时说明year-1是闰年
	for(int i=1;i<m;i++)
		sum += days[i];
	sum += d-1;
	return sum%7; // 返回这天是星期几 
}

// 求得第i行配置信息所生成的所有日期 v[4] 
// v[0]~v[3]不进行判断,只把tmp与pre中的内容组合起来,所有的判断放在v[4]中
void fillVector(vector<vector<LL> > &v,vector<LL>tmp,int j,LL mul)
{
	if(j==0) v[j] = tmp; // v仅用于该行的配置信息,不会覆盖 
	else{
		vector<LL> pre = v[j-1]; // 表示前面1轮求得的数字范围
		if(j!=4) // 不是最后1项 -- 求得该配置项和前面求得日期分别组合后的结果 
			for(int k=0;k<tmp.size();k++)
				for(int l=0;l<pre.size();l++)
					v[j].push_back(tmp[k]*mul+pre[l]);  // 该配置项的数字添在左边(其右边未满足2位的自动添0) 
	
		else{ // 是最后1项 需要综合所有配置项的信息求出 年 
			int judge[7] = {0};
			for(int k=0;k<tmp.size();k++)
				judge[tmp[k]] = 1; // 满足的星期 标明为1
			for(int l=0;l<pre.size();l++){ // 遍历已求得的所有满足配置项的日期(缺少年份) 
				int month = pre[l]/1000000,day=pre[l]/10000%100; // 求得月份和天数
				for(int year = firstYear;year<=lastYear;year++){
					int dow = getDay(year,month,day); // 判断该日期是否合法 
					if(dow!=-1 && judge[dow]) // 该日期合法且星期这一条件也满足
						v[4].push_back(year*mul+pre[l]); // v[4]为满足第i行的配置信息条件的最终日期 
				}	
			}
		}
	} 
}

int main()
{
	cin>>n;
	LL begin,end;  // LL很长,要放在main函数里(放在全局变量会报错) 
	cin>>begin>>end; // 输入第一行的数据 
	firstYear = begin/100000000;  // 开始年份
	lastYear = end/100000000; // 结束年份
	
	// 把12个月份和7个星期的英文转化为数字表示 
	for(int i=0;i<12;i++)
		mp[mon[i]] = i+1;
	for(int i=0;i<7;i++)
		mp[week[i]] = i;
	// 输入 n*6个string(配置项),每一行由一个 vector<string>保存信息,其大小为6,共有n个 vector<string> 
	vector<vector<string> > cron(n,vector<string>(6)); 
	for(int i=0;i<n;i++){ // 输入n行配置信息 
		for(int j=0;j<6;j++){ // 输入该行的6个配置项 
			cin>>cron[i][j];
		}
	}	
	
	// 依次处理n行配置信息,每行生成若干个pair<满足条件的日期,操作编号>存入结果ans的vector 
	for(int i=0;i<n;i++){
		vector<vector<LL> > v(5); // 5个vector<LL>,每个保存至当前配置项时可生成的日期数字,v[4]为完整日期数字的向量(仅用于该行配置信息) 
		LL mul = 1; // 处理需要乘的系数
		for(int j=0;j<5;j++){ // 依次处理处理该行的5个配置项 
			set<LL> valid; // 保存第(i+1)行配置的第(j+1)个配置项满足的数字集合(set去重) 
			findValue(valid,cron[i][j],j); // 将该配置项满足的值保存在valid集合当中;cron[i][j]为当前配置项;j指明配置项的种类 
			vector<LL> tmp; // 将valid集合里的元素转移到tmp向量中(方便和后面的双重vector统一格式) 
			for(set<LL>::iterator it=valid.begin();it!=valid.end();it++)
				tmp.push_back(*it); 
			fillVector(v,tmp,j,mul); // v保存满足条件的日期;满足的数值结果tmp向量;j类配置项;mul相乘系数(新增的数字向左)
			mul *= 100; // 第j个配置项依次向左移动mul位,从而生成新日期表示		 
		}
		for(int j=0;j<v[4].size();j++) // 可能会包含超过题目给出的日期,最后输出时再判断 
			ans.push_back(make_pair(v[4][j],i)); // v[4]中的每一个日期都对应i操作 				
	} 
	
	sort(ans.begin(),ans.end()); // 按日期先后顺序排序(由于前面是按每行的配置信息先后存入日期的,可以保证多个日期相同时,按照输入给出的操作顺序输出)	 
	vector<pair<LL,int> >::iterator it;
	for(it=ans.begin();it!=ans.end();it++)
		if(it->first>=begin && it->first<end) // 日期满足题目给定条件[first,end)
			cout<<it->first<<" "<<cron[it->second][5]<<endl;  // 按顺序输出日期及操作 
		
	return 0;
}  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值