题意:
题目描述
TA团队每周都会有很多任务,有的可以单独完成,有的则需要所有人聚到一起,开过会之后才能去做。但TA团队的每个成员都有各自的事情,找到所有人都有空的时间段并不是一件容易的事情。
给出每位助教的各项事情的时间表,你的任务是找出所有可以用来开会的时间段。
输入格式
第一行一个数T(T≤100),表示数据组数。
对于每组数据,第一行一个数m(2 ≤ m ≤ 20),表示TA的数量。
对于每位TA,首先是一个数n(0≤ n≤100),表示该TA的任务数。接下来n行,表示各个任务的信息,格式如下
YYYY MM DD hh mm ss YYYY MM DD hh mm ss “some string here”
每一行描述的信息为:开始时间的年、月、日、时、分、秒;结束时间的年、月、日、时、分、秒,以及一些字符串,描述任务的信息。
数据约定:
所有的数据信息均为固定位数,位数不足的在在前面补前导0,数据之间由空格隔开。
描述信息的字符串中间可能包含空格,且总长度不超过100。
所有的日期时间均在1800年1月1日00:00:00到2200年1月1日00:00:00之间。
为了简化问题,我们假定所有的月份(甚至2月)均是30天的,数据保证不含有不合法的日期。
注意每件事务的结束时间点也即是该成员可以开始参与开会的时间点。
输出格式
对于每一组数据,首先输出一行"Scenario #i:",i即表明是第i组数据。
接下来对于所有可以用来开会的时间段,每一个时间段输出一行。
需要满足如下规则:
在该时间段的任何时间点,都应该有至少两人在场。
在该时间段的任何时间点,至多有一位成员缺席。
该时间段的时间长度至少应该1h。
所有的成员都乐意一天24h进行工作。
举个例子,假如现在TA团队有3位成员,TT、zjm、hrz。
那么这样的时间段是合法的:会议开始之初只有TT和zjm,后来hrz加入了,hrz加入之后TT离开了,此后直到会议结束,hrz和zjm一直在场。
要求:
1.输出满足条件的所有的时间段,尽管某一段可能有400年那么长。
2.时间点的格式为MM/DD/YYYY hh:mm:ss。
3.时间段的输出格式为"appointment possible from T0 to T1",其中T0和T1均应满足时间点的格式。
4.严格按照格式进行匹配,如果长度不够则在前面补前导0。
5.按时间的先后顺序输出各个时间段。
6.如果没有合适的时间段,输出一行"no appointment possible"。
7.每组数据末尾须打印额外的一行空行。
思路:
首先是读入数据:用结构体time来存储年月日时分秒,六个数据都是int型,自动忽略01中的0。同时为time编写了构造函数和重载==、!=、<、判断两个time之间是否差了一小时以上的函数。
用结构体zhujiao来存储每个人的忙碌时间,vector< time > bg为事情开始的时间,vector< time >ed为事情结束的时间,两者一一对应。读入数据时直接忽略掉后面跟的字符串。
用map<time,int> mp来存储所有时间点以及忙碌人数,比如mp[time1]=1表示从time1到time1的下一个time之间有1个人忙碌。在此思想上使用前缀和和差分,若要在忙碌时间[leftT,rightT)区间上加上一人数,可以让mp[leftT]++,mp[rightT]–。之后前缀和sum就是当前元素的忙碌人数。
接下来用尺取法判断区间是否满足最多有maxRs个人忙碌且区间长度大于1h。若助教m只有2个人,则maxRs=0,否则maxRs=1。用两个迭代器it1,it2来遍历mp,用sum=0来记录前缀和。若满足最多有maxRS个人忙碌的要求,则it2++;若不满足,则判断当前区间it1~it2是否满足长度大于1h,然后it2++,it1=it2。
最后输出的格式也需要注意一下,因为是int类型。比如说1h1min1s就要输出01:01:01。这里用了output(int i)函数,如果i<10,就输出0和i;否则输出i。
总结:
一道很复杂的模拟题,综合性比较强。在限定时间里没有写出来,在后续补题时又重构了一下思路来求解。数据结构map的选择很重要,因为要记录关键时间点并排序、在此基础上进行其他操作,用其他的数据结构可能会超时,且更麻烦。最后还需注意,因为time需要进行比较,不能用string来存储年月日时分秒,应该用int,不然会超时。(调了好久TLE,最后才发现应该把string改int,这真的能好好长个记性)
代码:
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <map>
using namespace std;
int n,m,t;
struct time
{
int y,m,d,h,mi,s; //用string会超时
time() { }
time(int y1,int m1,int d1,int h1,int mi1,int s1)
{
y=y1,m=m1,d=d1,h=h1,mi=mi1,s=s1;
}
bool operator == (const time &t) const
{
if(y==t.y&&m==t.m&&d==t.d&&h==t.h&&mi==t.mi&&s==t.s)
return true;
else return false;
}
bool operator != (const time &t) const
{
if(y!=t.y||m!=t.m||d!=t.d||h!=t.h||mi!=t.mi||s!=t.s)
return true;
else return false;
}
bool operator < (const time &t) const
{
return y==t.y ? (m==t.m ? (d==t.d ? (h==t.h ? (mi==t.mi ? (s==t.s ? false : s<t.s) : mi<t.mi) : h<t.h) : d<t.d) : m<t.m) : y<t.y;
}
bool panduan(const time &t) //this->t长度是否大于1h
{
int y1=y,m1=m,d1=d,h1=h,mi1=mi,s1=s;
int y2=t.y,m2=t.m,d2=t.d,h2=t.h,mi2=t.mi,s2=t.s;
if(y2-y1>1||m2-m1>1||d2-d1>1||h2-h1>1)
return true;
else
{
unsigned long long int sum=0;
if(s2>=s1) sum=sum+s2-s1;
else mi2--,sum=sum+60+s2-s1;
if(mi2>=mi1) sum=sum+(mi2-mi1)*60;
else h2--,sum=sum+(60-mi2-mi1)*60;
if(h2>=h1) sum=sum+(h2-h1)*3600;
else d2--,sum=sum+(24+h2-h1)*3600;
if(sum>=3600) return true;
if(d2>=d1) sum=sum+(d2-d1)*24*3600;
else m2--,sum=sum+(30+d2-d1)*24*3600;
if(sum>=3600) return true;
if(m2>=m1) sum=sum+(m2-m1)*30*24*3600;
else y2--,sum=sum+(12+m2-m1)*30*24*3600;
if(sum>=3600) return true;
sum=sum+(y2-y1)*12*30*24*3600;
if(sum>=3600) return true;
else return false;
}
}
};
struct zhujiao
{
vector <time> bg; //事情开始
vector <time> ed; //事情结束
};
zhujiao zj[25];
vector<time> l,r; //总空闲时间
map<time,int> mp;
void output(int i)
{
if(i<10) cout<<0<<i;
else cout<<i;
}
int main()
{
cin>>t;
for(int zs=1; zs<=t; zs++)
{
cin>>m;
//清空
l.clear(),r.clear(),mp.clear();
for(int j=1; j<25; j++)
zj[j].bg.clear(),zj[j].ed.clear();
for(int i=1; i<=m; i++)
{
cin>>n;
//读入第i位TA数据
for(int j=1; j<=n; j++)
{
int y,m,d,h,mi,s;
cin>>y>>m>>d>>h>>mi>>s;
zj[i].bg.push_back(time(y,m,d,h,mi,s));
mp[time(y,m,d,h,mi,s)]=0;
cin>>y>>m>>d>>h>>mi>>s;
zj[i].ed.push_back(time(y,m,d,h,mi,s));
mp[time(y,m,d,h,mi,s)]=0;
char th[110];
cin.get(th,110);
}
}
mp[time(1800,1,1,0,0,0)]=0;
mp[time(2200,1,1,0,0,0)]=0;
//处理
//遍历所有忙碌时间[leftT,rightT),在map中对应区间加上一人数
//利用差分求解leftT +=1,rightT -=1
for(int i=1; i<=m; i++)
{
for(int j=0; j<zj[i].bg.size(); j++)
{
time leftT=zj[i].bg[j],rightT=zj[i].ed[j];
mp[leftT]++;
mp[rightT]--;
}
}
//调试
/*map<time,int>::iterator ts=mp.begin();
for(; ts!=mp.end(); ts++)
cout<<ts->first.y<<" "<<ts->first.m<<" "<<ts->first.d<<" "<<ts->first.h
<<" "<<ts->first.mi<<" "<<ts->first.s<<" "<<ts->second<<endl;*/
//判断区间是否满足最多有maxRS个人且长度大于1h
int maxRs;
if(m==2) maxRs=0;
else maxRs=1;
//尺取法遍历
map<time,int>::iterator it1=mp.begin(),it2=mp.begin();
int sum=0;
while(it1!=mp.end()&&it2!=mp.end())
{
sum=sum+it2->second;
if(sum<=maxRs) //满足要求
{
it2++;
if(it2==mp.end())
{
it2--;
time lt=it1->first,rt=it2->first;
if(lt.panduan(rt)) //大于1h,将其加入到vector
l.push_back(lt),r.push_back(rt);
it2++,it1=it2;
}
}
else //判断当前it1~it2是否满足区间,之后跳过it2
{
sum=sum-it2->second;
if(sum>maxRs) //没有区间,跳至下一个
sum=sum+it2->second,it2++,it1=it2;
else //有区间,判断是否长度大于1h
{
sum=sum+it2->second;
time lt=it1->first,rt=it2->first;
if(lt.panduan(rt)) //大于1h,将其加入到vector
l.push_back(lt),r.push_back(rt);
it2++,it1=it2;
}
}
}
//输出
cout<<"Scenario #"<<zs<<":"<<endl;
if(l.empty())
cout<<"no appointment possible"<<endl;
else
{
for(int i=0; i<l.size(); i++)
{
cout<<"appointment possible from ";
output(l[i].m);
cout<<"/";
output(l[i].d);
cout<<"/";
output(l[i].y);
cout<<" ";
output(l[i].h);
cout<<":";
output(l[i].mi);
cout<<":";
output(l[i].s);
cout<<" to ";
output(r[i].m);
cout<<"/";
output(r[i].d);
cout<<"/";
output(r[i].y);
cout<<" ";
output(r[i].h);
cout<<":";
output(r[i].mi);
cout<<":";
output(r[i].s);
cout<<endl;
}
}
cout<<endl;
}
}