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