今天偶然遇到了一个有点意思的问题,将它转化成了题目,有点令人怀念:
有一张活动排期表,表上有n组活动的排期。其中,每组活动都会开启若干次,每个活动都有一个唯一id和一个开启时间a,关闭时间b。同组活动不能在相同时间内开启两个及以上。如果同组活动同时开启,则会产生冲突,活动开启失败。问:当前活动表内是否存在冲突?如果存在,是哪几个冲突?(如果活动表中id为x的活动和id为y的活动冲突,且x<y,则y是冲突的活动)
输入:只有一组数据,数据分为若干行,每行数据包含本次活动的唯一id,这个活动所属组号i,活动开启时间a,活动关闭时间b。每组数据内部每个参数用制表符" "分隔。各行唯一id保证从小到大排列。
输出:首行输出“Success”或“Failed”,表示活动表排期成功或失败。
如果失败,接下来输出所有冲突的活动。输出格式为“Error: 唯一id”。每个错误占一行。
不同组的错误按照组号从小到大输出。
ps:需保证id小的活动不产生冲突。比如,当x>y>z时,x与z冲突,y与z冲突,且x和y不冲突,则只输出一次“Error: z”。而如果x与y冲突,x与z也冲突,则y与z都需要输出,输出:
“Error: y”
“Error: z”
输入样例:
1 1 1 10
2 2 1 10
3 1 12 15
4 1 5 11
输出样例:
Failed
Error: 4
看到这个题目,很容易想到,首先将这些输入数据按照活动组号分组,比如输入样例1,3,4行为一组,第2行为一组。
然后在组内进行两两比较,第1行和第3行比较,因为1-10和12-15没有重合,所以第1行和第3行不冲突。第1行和第4行比较,因为5>=1且5<=10,所以1-10和5-11有重合,这两行冲突了。然后第3行和第4行比较,这两行没冲突。
这种方法花费的时间与输入样例中活动的组数和每组活动的行数有关,为∑((ai)^2)/2。i为每组数据的组号,ai为每组数据包含的行数。估算时间复杂度为O(n^2),n为行数。
由于“ps”里的内容,可以使用一种优化——当前数据和前面的数据产生冲突时,记录Error然后直接返回。比如第4行数据和第1行数据比较后,不再和第3行数据比较。
这是我想起来的第一种算法。这种算法的优点是简单明了,一个一个比较就好。缺点是当每组数据规模巨大时,消耗的时间成平方增长。
我好不容易遇到一个有点意思的问题,怎么能这样放弃?于是我稍微思考了一会儿。想出了一个新办法:
之后只针对每组数据组内的处理进行描述,因为组与组之间没有关联,不需要额外处理。
我们设置一个bool型数组,初始全部置为0。每取一行数据,就以这组数据开始时间到结束时间之间的所有数字为下标,遍历数组内这些下标的值,如果存在一个值为1,则记录这行数据为error,否则将数组内这些下标的值置为1。
第一组数据,初始有一个数组flag=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]。
读入第1行后,置为flag=[1,1,1,1,1,1,1,1,1,1,0,0,0,0,0]。
读入第3行后,置为flag=[1,1,1,1,1,1,1,1,1,1,0,1,1,1,1]。
读入第4行时,因为5-11中,flag[5-10]都已经为1了,所以产生冲突。
使用这种方法,处理每组数据花费的时间为组内每行数据的(结束时间-开始时间+1)之和,所有组的时间为∑(bi-ai+1)。估算时间复杂度为O(n*(b-a)),n为行数。
只需要(b-a)<n,这种算法消耗的时间就极有可能小于第一种算法。
但这就结束了吗?
当然还没有结束!
沿着这条思路走下去,我们的需求已经很明确了!“区间修改”+“区间查询”!没错,就是它——线段树!
使用线段树,可以使单次区间修改和查询的时间复杂度变为O(log(b-a)),那么最后这个算法的总时间复杂度为O(n*log(b-a))!
几乎可以肯定,这种算法消耗的时间一定小于前两种!
我选择了第一种算法。
写第一种算法连1分钟都不需要,写线段树保守估计一个小时起,毕竟已经好久没写过了。可需要处理的数据量只有几百条。无论哪种算法都可以在0.01秒之内解决。
而且,这次用的是python,我从没用python写过复杂的数据结构——确切的说,我之前连python自带数据结构都不清楚,语言功底极差。
最后我花了一个小时弄明白了python自带的数据结构,花了5分钟写完了全部代码,然后调通代码又花了1个小时。
代码(因需求与题目描述有少许不同,所以有些差异)——
1 #!/usr/bin/python 2 # -*- encoding:utf8 -*- 3 import sys, os, io 4 import string 5 import commands 6 import time, datetime 7 8 actArr = {} 9 errArr = {} 10 11 def checkactivity(): 12 file = open('activity.txt') 13 14 errCnt = 0 15 linenum = 0 16 for fileLine in file: 17 18 19 linenum += 1 20 if (linenum <= 3): 21 continue 22 23 id = int(fileLine.strip(' ').split(' ')[0]) 24 enable = int(fileLine.strip(' ').split(' ')[2]) 25 group = int(fileLine.strip(' ').split(' ')[3]) 26 starttime = fileLine.strip(' ').split(' ')[4] 27 endtime = fileLine.strip(' ').split(' ')[5] 28 29 timeArray = time.strptime(fileLine.strip(' ').split(' ')[4], "%Y-%m-%d-%H:%M:%S") 30 starttime = int(time.mktime(timeArray)) 31 timeArray = time.strptime(fileLine.strip(' ').split(' ')[5], "%Y-%m-%d-%H:%M:%S") 32 endtime = int(time.mktime(timeArray)) 33 34 # print enable,group,starttime,endtime 35 if(enable == 0): 36 continue 37 38 tmpArr = [id, enable, group, starttime, endtime] 39 40 actArrTmp = [] 41 if(group in actArr): 42 for arrLine in actArr[group]: 43 44 45 if( (arrLine[3] <= starttime and arrLine[4] >= starttime) or ( arrLine[3] <= endtime and arrLine[4] >= endtime ) ): 46 errArr[errCnt] = [0, arrLine[0], enable, group, arrLine[3], arrLine[4]] 47 errCnt = errCnt+1 48 errArr[errCnt] = [1, id, enable, group, starttime, endtime] 49 errCnt = errCnt+1 50 break 51 actArrTmp = actArr[group] 52 53 actArrTmp.append(tmpArr) 54 actArr[group] = actArrTmp 55 #print actArr[group] 56 57 for errIndex in errArr: 58 print 'error id: ',errArr[errIndex][1] 59 60 if(errCnt == 0): 61 print "Success!" 62 63 file.close() 64 65 checkactivity()