2021-03-18

CCF-CSP Crontab

非常坑的一题,可能是因为我菜吧。
考的的东西很多,把几种模拟题都揉在一起了,比如:

  1. 日期处理,包括枚举分钟,计算某天是星期几,后者可以枚举天,也有公式,但是我记不住,哈哈。
  2. 字符串解析,这个不用说了,大模拟必考。
  3. 解析后建立满足题目意思的数据结构。

思路:首先注意日期的表示法,我用的是年月日时分表示法,而不是弄成一个整数,实际上我只会这一种表示法,并且在算法笔记上学过练过。然后把控制表(即Crontab)输入,解析,建立可以方便的查询的数据结构。还是老办法,用enum标记每种情况,然后不同情况不同处理。这里我分了三种情况:
4. Star,即任意值。
5. EQL,必须严格相等的一个值(单个值)。
6. RNG,可以多个值,每个值可以是范围。就是最复杂的情况,可以有逗号和横杠。注意到,单个值其实可以表示成横杠表示法,即4 = 4-4,这样就统一了,不用再嵌套一个区分区间和严格相等的结构。

然后比较坑的就是计算某天是星期几了,这是我最怕的一个,因为老是算错。还好题目给了提示,19700101是星期四,然后笨办法累加得出s和t的星期,其实只需要计算s的星期即可。然后就没什么难点了,就是代码量略大,因为糅合了日期处理和字符串解析的代码。

#include <cstdio>
#include <vector>
#include <utility>
#include <cassert>
#include <string>
#include <cstring>
using namespace std;

/* run this program using the console pauser or add your own getch, system("pause") or input loop */
#define MAXL 105
char line[MAXL];

bool IsLeap(int year) {
	return (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0);
}

const int Day[]={0,31,28,31,30,31,30,31,31,30,31,30,31};

const char MonName[][5]={"","jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"};
const char WeekName[][5]={"sun","mon","tue","wed","thu","fri","sat"};

int MonthDay(int year, int mon) {
	// 注意,闰年的二月加一天。 
	return Day[mon]+(mon==2&&IsLeap(year));
}

int GetWeek(int y, int m, int d) {
	int yy=1970;
	int mm=1;
	int dd=1;
	int ww=4;
	// 注意,这里出bug了,条件是或,不是与。 
	while (yy<y || mm<m || dd<d) {
		++dd;
		ww=(ww+1)%7;
		if (dd==MonthDay(yy, mm)+1) {
			dd=1;
			++mm;
		}
		if (mm==13) {
			mm=1;
			++yy;
		}
	}
	return ww;
}

struct Time {
	int y, mon, d, h, min;
	int w; // week
	
	void Read() {
		scanf("%04d%02d%02d%02d%02d",&y,&mon,&d,&h,&min);
		w=GetWeek(y, mon, d);
//		printf("w %d\n", w);
	}
	void Print() {
		printf("%04d%02d%02d%02d%02d",y,mon,d,h,min);
	}
	friend bool operator==(const Time& a, const Time& b) {
		return a.y==b.y &&
			a.mon==b.mon &&
			a.d==b.d &&
			a.h==b.h &&
			a.min==b.min;
	}
	void inc() {
		// 自增1分钟。
		min++;
		if (min==60) {
			min=0;
			h++;
		}
		if (h==24) {
			h=0;
			++d;
			w=(w+1)%7;
		}
		if (d==MonthDay(y, mon)+1) {
			d=1;
			++mon;
		}
		if (mon==13) {
			mon=1;
			++y;
		}
	}
};

enum {
	STAR,
	EQL, // equal
	RNG, // range.
};
const char Name[][10]={"star","eql","rng"};

typedef pair<int,int> Range;

struct Cond {
	int type;
	int eql; // EQL.
	vector<Range> rng;

	bool Match(int val) {
		int i;
		switch (type) {
			case STAR:return true;
			case EQL:return eql==val;
			default:
				for (i=0;i<rng.size();++i) {
					Range p=rng[i];
					if (p.first<=val&&val<=p.second) {
						return true;
					}
				}
				return false;
		}
	}
};

#define MAXC 5

struct Node {
	// 所有的条件。 
	Cond conds[MAXC];
	char cmd[MAXL];
	
	Node(){
	}
	bool Match(const Time& t) {
		return conds[0].Match(t.min) &&
		conds[1].Match(t.h) &&
		conds[2].Match(t.d) &&
		conds[3].Match(t.mon) &&
		conds[4].Match(t.w);
	}
};

vector<Node> tab;

// 把从i开始的字符串转换为数字,考虑数字和缩写混合的情况。 
int Parse3(char str[], int& i, int len) {
	if (isdigit(str[i])) {
		int n=0;
		while (i<len && isdigit(str[i])) {
			n=n*10+(str[i]-'0');
			++i;
		}
		return n;
	}
	string buf;
	while (i<len && isalpha(str[i])) {
		buf.push_back(tolower(str[i]));
		++i;
	}
	for (int i=1;i<=12;++i) {
		if (buf == MonName[i]) {
			return i;
		}
	}
	for (int i=0;i<=6;++i) {
		if (buf == WeekName[i]) {
			return i;
		}
	}
	return -1;
}

void Parse2(Cond& cd, char str[], int len) {
	if (len==1 && str[0]=='*') {
		cd.type=STAR;
		return;
	}
	bool rng=false;
	// 看是否为单独取值。
	for (int i=0;i<len;++i) {
		if (str[i]==','||str[i]=='-') {
			rng=true;
			break;
		}
	}
	if (!rng) {
		// eql
		cd.type=EQL;
		int i=0;
		cd.eql=Parse3(str, i, len);
		assert(i==len);
		return;
	}
	cd.type=RNG;
	// 逗号分割的列表。
	int i=0;
	while (i<len) {
		int b=Parse3(str, i, len);
		int e=b;
		if (str[i] == '-') {
			++i;
			e=Parse3(str, i, len);
		}
		Range r={b,e};
		cd.rng.push_back(r);
		if (i>=len) {
			break;
		}
		if (str[i]==',') {
			++i;
		}
	}
}

void Parse(Node& node) {
	for (int i=0;i<MAXC;++i) {
		char str[MAXL];
		scanf("%s", str);
		Parse2(node.conds[i], str, strlen(str));
	}
	scanf("%s", node.cmd);
}

int N;

void Print() {
	for (int i=0;i<N;++i) {
		printf("%s", tab[i].cmd);
		for (int j=0;j<MAXC;++j) {
			Cond& cd=tab[i].conds[j];
			int type=cd.type;
			printf(" %s", Name[type]);
			if (type==EQL) {
				printf(" %d", cd.eql);
			} else if (type==RNG) {
				for (int k=0;k<cd.rng.size();++k) {
					Range r=cd.rng[k];
					printf(" <%d,%d>", r.first,r.second);
				}
				
			}
		}
		puts("");
	}
}

int main(int argc, char** argv) {
	scanf("%d",&N);
	Time a, b;
	a.Read();
	b.Read();
	
	tab.resize(N);
	for (int i=0;i<N;++i) {
		Parse(tab[i]);
	}
	
	for (;!(a==b);a.inc()) {
		for (int i=0;i<N;++i) {
			if (tab[i].Match(a)) {
				a.Print();
				printf(" %s\n", tab[i].cmd);
			}
		}
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值