样例输入
3 201711170032 201711222352
0 7 * * 1,3-5 get_up
30 23 * * Sat,Sun go_to_bed
15 12,18 * * * have_dinner
样例输出
201711170700 get_up
201711171215 have_dinner
201711171815 have_dinner
201711181215 have_dinner
201711181815 have_dinner
201711182330 go_to_bed
201711191215 have_dinner
201711191815 have_dinner
201711192330 go_to_bed
201711200700 get_up
201711201215 have_dinner
201711201815 have_dinner
201711211215 have_dinner
201711211815 have_dinner
201711220700 get_up
201711221215 have_dinner
201711221815 have_dinner
问题链接:CCF201712-3 Crontab
问题分析:
这是一个模拟题,逻辑太复杂了。
一般而言,模拟题的关键在于数据表示,有了正确合理的数据表示,基本上就成功了。
编程采用自顶向下逐步细化的结构化编程思想。这样使得程序的每一个局部逻辑都变得简单易懂。
程序说明:
结构Crontab的数组cmd[]是没有必要的,单变量就可以了。
用C++的pair类型是一种好的选择,用它来表示一个范围。任意的范围就用<-1,-1>来表示。从数学意义上说,pair类型可以称为序偶,其实其用途十分广泛。
分钟,小时,日,月份和星期,都有可能有若干个范围,所以用向量来存储是十分合适的。用数组表示也应该没有问题,只是数组要开得大一些,会浪费一部分存储。
程序中,很多代码是有关文本处理的。文本处理在CCF考试中非常常见,需要了然于心。
问题充满陷阱,比如月份和日期的简写,有可能是不规范(首字母大写,其他字母小写才是规范的)的,需要大小写转换后进行比较。不进行处理就比较只能得85分。
有关年月日算星期几的问题,用蔡勒公式进行计算是标准的,也可以用简易计算来替代。考试时一般来说写出相应的程序是困难的,似乎只能编写一个简单的替代函数代用一下。可以用模拟法来计算,逻辑也是比较麻烦的。
计算日期时间的组合是比较麻烦的事情,需要回溯。因为日期和时间各自的进制不同,不全是10进制,所以难以用循环计数进行处理,需要找到一种方法来解决。
程序中,尽可能把功能通用化,并且封装到函数中。
程序虽然比较长,但是各个部分的逻辑相似,局部还是比较简单的,写起来也比较容易。
代码不够简洁另外写了一版。
参考链接:
题记:
职业程序员通常追求程序的通用性。
提交后得100分的C++语言程序(简洁版)如下:
/* CCF201712-3 Crontab */
#include <iostream>
#include <vector>
#include <queue>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
using namespace std;
const char *weeks_months[]={"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
const int days[]={0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int leapyear(int year, int month)
{
if(month == 2)
return ( ((year%4==0) && (year%100!=0)) || (year%400==0) ) ? 1 : 0;
else
return 0;
}
// Crontab
const int V = 5;
vector<pair<int, int> > v[V]; // 分别表示:分钟,小时,日,月份,星期
string cmd;
struct CMD {
int id;
long long time;
string cmd;
bool operator < (const CMD &a) const //要按时间和先后顺序排好
{
return (time == a.time) ? id > a.id : time > a.time;
}
};
char buf[256];
int getval(char t[])
{
int i;
t[0] = toupper(t[0]);
for(i = 1; t[i]; i++)
t[i] = tolower(t[i]);
for(i = 0; i < 12 + 7; i++)
if(strcmp(t, weeks_months[i]) == 0)
break;
if(i < 12 + 7)
return i < 7 ? i : i - 6;
else
return -1;
}
void setsubval(char s[], vector<pair<int, int> >& v)
{
int p1 = 0, p2 = 0;
for(int i = 0; s[i]; i++)
if(s[i] == '-') {
s[i] = '\0';
p2 = i + 1;
break;
}
int val1, val2;
if(p1 == p2) {
if(isdigit(s[0]))
val1 = atoi(s);
else
val1 = getval(s);
v.push_back(make_pair(val1, val1));
} else {
if(isdigit(s[0]))
val1 = atoi(s);
else
val1 = getval(s);
if(isdigit(s[p2]))
val2 = atoi(s + p2);
else
val2 = getval(s + p2);
v.push_back(make_pair(val1, val2));
}
}
void setval(char s[], vector<pair<int, int> >& v)
{
if(s[0] == '*')
v.push_back(make_pair(-1, -1));
else {
char *p = strtok(s, ",");
while(p) {
setsubval(p, v);
p = strtok(NULL, ",");
}
}
}
int myatoi(char t[], int b, int e)
{
int v = 0;
for(int i = b; i <= e; i++)
v = v * 10 + t[i] - '0';
return v;
}
bool judge(int m, vector<pair<int, int> >& v)
{
for(int i = 0; i < (int)v.size(); i++)
if(v[i].first == -1 || (v[i].first <= m && m <= v[i].second))
return true;
return false;
}
bool end_time_check(int y, int m, int d, int h, int mi, int ey, int em, int ed, int eh, int emi)
{
if(y < ey) return true;
if(m > em) return false;
if(m < em) return true;
if(d > ed) return false;
if(d < ed) return true;
if(h > eh) return false;
if(h < eh) return true;
if(mi > emi) return false;
if(mi < emi) return true;
return false;
}
// 适用于1582年10月15日之后, 因为罗马教皇格里高利十三世在这一天启用新历法
// 蔡勒公式:给定年月日,得到当天是星期几
int weekday(int year,int month,int day)
{
if(month == 1 || month == 2) {
month += 12;
year--;
}
int c = year / 100;
int y = year % 100;
int m = month;
int d = day;
int w = c / 4 - 2 * c + y + y / 4 + 26 * (m + 1) / 10 + d - 1;
if(w < 0)
return (w + (-w / 7 + 1) * 7) % 7;
return w % 7;
}
int main()
{
int n;
string s, t;
priority_queue<CMD> q;
cin >> n >> s >> t;
strcpy(buf, s.c_str());
int sy = myatoi(buf, 0, 3);
int sm = myatoi(buf, 4, 5);
int sd = myatoi(buf, 6, 7);
int sh = myatoi(buf, 8, 9);
int smi = myatoi(buf, 10, 11);
strcpy(buf, t.c_str());
int ey = myatoi(buf, 0, 3);
int em = myatoi(buf, 4, 5);
int ed = myatoi(buf, 6, 7);
int eh = myatoi(buf, 8, 9);
int emi = myatoi(buf, 10, 11);
for(int i = 0; i < n; i++) {
string ss;
// 分别处理:分钟,小时,日,月份,星期
for(int j = 0; j < V; j++) {
v[j].clear();
cin >> ss;
strcpy(buf, ss.c_str());
setval(buf, v[j]);
}
// Command
cin >> cmd;
int k = sm, l = sd, m = sh, n = smi; // 分别作为月份、日、小时和分钟的循环变量
for(int j = sy; j <= ey; j++, k=1) // 年循环处理
for(; k <= 12; k++, l = 1)
if(judge(k, v[3]))
for(; l <= days[k] + leapyear(j, k); l++, m = 0)
if(judge(l, v[2]) && judge(weekday(j, k, l), v[4]))
for(; m < 24; m++, n = 0)
if(judge(m, v[1]))
for(; n < 60; n++) {
if(!end_time_check(j, k, l, m, n, ey, em, ed, eh, emi))
break;
if(judge(n, v[0])) {
CMD tmp;
tmp.id = i;
tmp.time = (long long)j * 100000000 + (long long)k * 1000000 + (long long)l * 10000 + (long long)m * 100 + n;
tmp.cmd = cmd;
q.push(tmp);
}
}
}
while(!q.empty()) {
CMD tmp = q.top();
q.pop();
cout << tmp.time << " " << tmp.cmd << endl;
}
return 0;
}
提交后得100分的C++语言程序如下:
/* CCF201712-3 Crontab */
#include <iostream>
#include <vector>
#include <queue>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
using namespace std;
const char *months[]={"", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
const char *weeks[]={"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
const int days[]={0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int leapyear(int year, int month)
{
if(month == 2)
return ( ((year%4==0) && (year%100!=0)) || (year%400==0) ) ? 1 : 0;
else
return 0;
}
const int N = 20;
struct Crontab {
vector<pair<int, int> > mi; // 分钟
vector<pair<int, int> > h; // 小时
vector<pair<int, int> > d; // 天数
vector<pair<int, int> > m; // 月份
vector<pair<int, int> > w; // 星期
string cmd;
} cmd[N];
struct CMD {
int id;
long long time;
string cmd;
bool operator < (const CMD &a) const //要按时间和先后顺序排好
{
return (time == a.time) ? id > a.id : time > a.time;
}
};
char buf[256];
int getval(char t[])
{
int i;
t[0] = toupper(t[0]);
for(i = 1; t[i]; i++)
t[i] = tolower(t[i]);
for(i = 1; i <= 12; i++)
if(strcmp(t, months[i]) == 0)
break;
if(i <= 12)
return i;
else {
for(i = 0; i <= 6; i++)
if(strcmp(t, weeks[i]) == 0)
break;
if(i <= 6)
return i;
}
return -1;
}
void setsubval(char s[], vector<pair<int, int> >& v)
{
int p1 = 0, p2 = 0;
for(int i = 0; s[i]; i++)
if(s[i] == '-') {
s[i] = '\0';
p2 = i + 1;
break;
}
int val1, val2;
if(p1 == p2) {
if(isdigit(s[0]))
val1 = atoi(s);
else
val1 = getval(s);
v.push_back(make_pair(val1, val1));
} else {
if(isdigit(s[0]))
val1 = atoi(s);
else
val1 = getval(s);
if(isdigit(s[p2]))
val2 = atoi(s + p2);
else
val2 = getval(s + p2);
v.push_back(make_pair(val1, val2));
}
}
void setval(char s[], vector<pair<int, int> >& v)
{
if(s[0] == '*')
v.push_back(make_pair(-1, -1));
else {
char *p = strtok(s, ",");
while(p) {
setsubval(p, v);
p = strtok(NULL, ",");
}
}
}
int myatoi(char t[], int b, int e)
{
int v = 0;
for(int i = b; i <= e; i++)
v = v * 10 + t[i] - '0';
return v;
}
bool judge(int m, vector<pair<int, int> >& v)
{
for(int i = 0; i < (int)v.size(); i++)
if(v[i].first == -1 || (v[i].first <= m && m <= v[i].second))
return true;
return false;
}
bool end_time_check(int y, int m, int d, int h, int mi, int ey, int em, int ed, int eh, int emi)
{
if(y < ey) return true;
if(m > em) return false;
if(m < em) return true;
if(d > ed) return false;
if(d < ed) return true;
if(h > eh) return false;
if(h < eh) return true;
if(mi > emi) return false;
if(mi < emi) return true;
return false;
}
// 适用于1582年10月15日之后, 因为罗马教皇格里高利十三世在这一天启用新历法
// 蔡勒公式:给定年月日,得到当天是星期几
int weekday(int year,int month,int day)
{
if(month == 1 || month == 2) {
month += 12;
year--;
}
int c = year / 100;
int y = year % 100;
int m = month;
int d = day;
int w = c / 4 - 2 * c + y + y / 4 + 26 * (m + 1) / 10 + d - 1;
if(w < 0)
return (w + (-w / 7 + 1) * 7) % 7;
return w % 7;
}
int main()
{
int n;
string s, t;
priority_queue<CMD> q;
cin >> n >> s >> t;
strcpy(buf, s.c_str());
int sy = myatoi(buf, 0, 3);
int sm = myatoi(buf, 4, 5);
int sd = myatoi(buf, 6, 7);
int sh = myatoi(buf, 8, 9);
int smi = myatoi(buf, 10, 11);
strcpy(buf, t.c_str());
int ey = myatoi(buf, 0, 3);
int em = myatoi(buf, 4, 5);
int ed = myatoi(buf, 6, 7);
int eh = myatoi(buf, 8, 9);
int emi = myatoi(buf, 10, 11);
for(int i = 0; i < n; i++) {
string ss;
// 分钟
cin >> ss;
strcpy(buf, ss.c_str());
setval(buf, cmd[i].mi);
// 小时
cin >> ss;
strcpy(buf, ss.c_str());
setval(buf, cmd[i].h);
// 天数
cin >> ss;
strcpy(buf, ss.c_str());
setval(buf, cmd[i].d);
// 月份
cin >> ss;
strcpy(buf, ss.c_str());
setval(buf, cmd[i].m);
// 周
cin >> ss;
strcpy(buf, ss.c_str());
setval(buf, cmd[i].w);
// Command
cin >> cmd[i].cmd;
int k = sm, l = sd, m = sh, n = smi;
for(int j = sy; j <= ey; j++, k=1)
for(; k <= 12; k++, l = 1)
if(judge(k, cmd[i].m))
for(; l <= days[k] + leapyear(j, k); l++, m = 0)
if(judge(l, cmd[i].d) && judge(weekday(j, k, l), cmd[i].w))
for(; m < 24; m++, n = 0)
if(judge(m, cmd[i].h))
for(; n < 60; n++) {
if(!end_time_check(j, k, l, m, n, ey, em, ed, eh, emi))
break;
if(judge(n, cmd[i].mi)) {
CMD tmp;
tmp.id = i;
tmp.time = (long long)j * 100000000 + (long long)k * 1000000 + (long long)l * 10000 + (long long)m * 100 + n;
tmp.cmd = cmd[i].cmd;
q.push(tmp);
}
}
}
while(!q.empty()) {
CMD tmp = q.top();
q.pop();
cout << tmp.time << " " << tmp.cmd << endl;
}
return 0;
}