CCF201712-3 Crontab(100分)【模拟+文本处理】



样例输入

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进制,所以难以用循环计数进行处理,需要找到一种方法来解决。

  程序中,尽可能把功能通用化,并且封装到函数中。

  程序虽然比较长,但是各个部分的逻辑相似,局部还是比较简单的,写起来也比较容易。

  代码不够简洁另外写了一版。

参考链接:

CCF201712-3 Crontab(100分)

题记

  职业程序员通常追求程序的通用性。

提交后得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;
}

  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值