UVA

来源:互联网 发布:诺基亚n900运行windows 编辑:程序博客网 时间:2024/06/03 06:38
/*  法一借鉴自:  http://blog.csdn.net/wangjinhaowjh/article/details/50551640*/#include <iostream>#include <vector>#include <map>#include <algorithm>#define rep(i, n) for (int i = 0; i < (n); i++)using namespace std;struct topic{int tid, num, t0, t, dt, num1; //num1为该主题最初的请求个数,num为当前剩余的请求个数 };struct person{int pid, k, last, busy, time; // last为客服上次处理请求的时间,busy标记是否忙碌,time为当前处理任务的剩余时间(不忙碌时为0) vector<int> tidk; // tidk保存客服可以处理的所有请求主题 ( 保存的是主题的 tid ) bool operator < (const person& a) const{if (last != a.last) last < a.last;else pid < a.pid;}};vector<topic> topics;vector<person> people;map<int, int> rel; // relationship key为tid,value为该主题在topics容器中的下标(从0开始)map<int, vector<person> > staff; // key为任务主题的tid,value为选定同一主题的所有客服map<int, int> service;//key为客服的标识符pid,value为客服的标号(从0开始)int if_solve(const topic &x, int t) //i 为当前处理任务的标号(非tid),t为当前时间 {if (t < x.t0) return 0; //不能比处理第一个请求的时间早 if (!x.dt) return x.num > 0; //如果该主题的请求连续不间断,则只要当前还需处理的数目大于0,就一定能处理if (!x.num) return 0; //请求数已为0,一定不能处理该主题的任务了if ((t - x.t0) / x.dt == (x.num1 - x.num - 1) ) return 0; //注意 (x.num1 - x.num - 1) 一定要加括号,否则会因为运算符的优先级而WA return 1;}int main(){int n, m, kase = 0;int i, j, k;while (cin >> n && n){int time = 0;topics.clear(); people.clear();rel.clear(); service.clear();rep(i, n) //输入n种主题及其对应信息 {topic a;cin >> a.tid >> a.num >> a.t0 >> a.t >> a.dt;a.num1 = a.num;topics.push_back(a);rel[a.tid] = i; //将主题的tid,与它在tipics中的下标建立对应关系 }cin >> m;rep(i, m) //输入m个客服及其对应信息 {person a;a.tidk.clear();a.last = a.busy = a.time = 0; cin >> a.pid >> a.k;service[a.pid] = i;int tid;rep(j, a.k){cin >> tid;a.tidk.push_back(tid);}people.push_back(a);}for (time = 0; ; time++){staff.clear();rep(i, m) //找到m个客服中,不忙的客服进行循环 if (!people[i].busy)rep(j, people[i].k) //遍历该客服可处理的k种类主题,并按优先级找到第一个可处理的主题 {int &tp = people[i].tidk[j]; //tp为,第i个客户可处理的优先级排第j的请求主题的tid if ( if_solve(topics[rel[tp]], time) ){if ( !staff.count(tp) )staff[tp] = vector<person>();staff[tp].push_back(people[i]);break; //每个有空的人,只要找到第一个可以处理的请求即可 }}map<int, vector<person> > ::iterator it;for (it = staff.begin(); it != staff.end(); it++){//*it 为 pair(tid, vector<person>) sort ( it->second.begin(), it->second.end() );int tp = service[it->second[0].pid]; // tp保存:对于同一个主题的tid,如果多人同时选择,最终处理该主题的客服是谁,并通过service的映射对应到该客户在people容器中的下标people[tp].busy = 1;people[tp].last = time;people[tp].time = topics[rel[it->first]].t; //该客户执行任务的剩余时间,被赋值为对该主题,处理一个请求的时间 topics[rel[it->first]].num--; //所处理主题对应的该主题请求个数自减}//对于每一个在处理任务的人,更新其任务的剩余时间 rep(i , m){if (people[i].busy) people[i].time--;if (!people[i].time) people[i].busy = 0;}int flag = 1; //如果所有人都处理完了任务,并且所有主题的剩余请求个数为0,那么可以退出,但是真正的处理完毕时间,还要在time的基础上加1rep(i, n)if (topics[i].num > 0){flag = 0; break;}rep(i, m)if (people[i].time > 0){flag = 0; break;}if (flag) break;}cout << "Scenario " << ++kase << ": All requests are serviced within " << time+1 << " minutes." << endl;}return 0;}


/*  法二(做完后去看了别人的方法):  http://blog.csdn.net/a197p/article/details/44199713    与法一相似,但也有不同,主要体现在    1. 取消了rel这个映射(原来是用来建立主题的tid和其在topics中下标的键值关系),取消了service(原来建立客服标识符pid和客服标号之间的键值关系);    该博主如何处理客服选择当前回复的主题?  1.1 排序所有客服(客服排序的标准:多个人同时处理一个任务时,该任务被谁处理,谁排前面)    1.2 对每个客服,按照其可执行任务的优先级循环一次(j循环),再按当前所有剩余任务执行一次(k循环),如果遇到第一个可以处理的任务(tid相同,且客服手头没有别的任务),则做 1.3 中的处理以后,循环到下一个客服,遍历完所有客服后执行1.4,如果没遇到,则在双层循环遍历完staff[i].pidk和topics中的所有主题,也会继续循环其他的客服    1.3 更新客服上次执行任务的时间、当前剩余时间,所接任务主题的剩余请求数,和need(need的含义:当前时刻下,执行时间最久的任务,执行完需要多久);此外,对于某个主题,如果请求数都被处理完(finished == num),则该主题可从主题的容器topics中删去,如果所有主题都执行完了,只要将当前时间,加上当前时间最长的任务还需要多久,便可退出循环,进行输出了    1.4   循环完所有客服以后,没退出则时间自增,need(need的含义:当前时刻下,执行时间最久的任务,执行完需要多久)自减直至为0,每一个客服的当前任务剩余时间自减直到为0    优点:map的键值关系比较少,降低了混淆的可能性,并且在排序和遍历时,省去了迭代器和其指针操作->,在对这些理解不太深刻时,用这种方法比较不易出错    缺点:三重循环,在数据很多时,容易TLE*/
/*  法三(做完后去看了别人的方法,并且自己也按照这个思路写了一次):  http://www.cnblogs.com/xienaoban/p/6798081.html    该博主的思路十分巧妙,虽然我看了许久许久才看懂,但真的想说,他的解法很精妙,值得反刍  并且,速度还从法一的 100ms,降到了法二的 10ms,可见法二在效率上也很令人惊叹    收获 || 总结:  1. C++ 用括号初始化变量(这种写法之前只是在 class 的构造函数的初始化列表中学到过,但是对于一般的变量,也是可以在其后面用括号初始化的)    如: int Scenario(0);    此外,关于C++初始化的小总结,以及C++11新增的新的初始化方式:  http://blog.csdn.net/k346k346/article/details/55194246     2.另外,该博主不是按时间一秒一秒增加来模拟的,而是每次都找出所有人的,状态变化临界点(从工作到休息,或是从休息到工作,的状态切换时间点),取其中的最小值,就能兼顾到所有人组成的整体的状态切换,这个角度十分巧妙,将以时间为主线考虑,变为了以人的工作状态为主线考虑         3. 没有用太多map,结构体中也没存那么多数据,仅存最关键的核心数据   (例如对于主题而言,重要的是每次处理所需的时间,以及每次请求分别在哪些时刻发出,因为只有发出请求以后,该请求才能被客服处理)    并且每次主题请求为空,直接出栈,且主题数自减,当减为0时,循环结束,运用queue的压入弹出、是否为空的判断,代替对主题和对客服判断的循环,提高效率    4. 此外,STL存在性能瓶颈,例如对map使用map[key]时,每次都需要重新查找,在这题中,博主的思路,就是将查找结果赋值给一个引用变量,这样接下来就不再需要每次用前都查找,提高了效率      作者其他的思路可见原博主代码后的解析*/  
#include <iostream>#include <vector>#include <queue>#include <algorithm>#include <map>#define rep(i, n) for (int i = 0; i < (n); i++)const int INF = 0x3f3f3f3f;using namespace std;struct topic{int t; //记录处理一个请求所用的时间queue <int> arrival; //arrival为topic的num个请求的依次抵达时间}_topic;struct person{int pid, next, last, k, tidk[22]; //next为下次有空的时间,last为上次开始处理主题的时间bool operator < (const person & t) const{if (last != t.last) return last < t.last;return pid < t.pid;} }_person;int n, m; //topic个数和客服人数int tid, num, t0, dt,  kase(0);map<int, topic>rel; //主题的tid与主题的对应关系 vector<person> people;int main(){cin.tie(0);cin.sync_with_stdio(false);_person.next = 0;while (cin >> n && n){int time(INF), needtime(0);rel.clear(); people.clear();rep(i, n){cin >> tid >> num >> t0 >> _topic.t >> dt;time = min(time, t0); //time记录当前开始时间的最小值auto &now(rel[tid] = _topic); //用引用,减少map查找的时间,提高效率rep(i, num) //该主题的所有请求时间,压入tidk {now.arrival.push(t0);t0 += dt;}}cin >> m;rep(i, m){cin >> _person.pid >> _person.k;rep(j, _person.k)cin >> _person.tidk[j];people.push_back(_person);} while (n){int jumpt = INF; //jumpt其实含义为:对整体而言,每次最早出现的状态变化临界点sort(people.begin(), people.end());for (auto &p : people){int pti(INF); //pti的意义:开始空闲”或“可能有事情做”的时间,即为可能的状态变化临界时间点(之所以是“可能”,因为每次都要按照“上一次开始干活的时间与ID”把每个人排序,所以他的活可能被抢,依然是“无事可干”状态)if (p.next > time) pti = p.next;  //若这人正在处理一个topic,则pti=处理完本topic的时间else{rep(i, p.k){auto &t(rel[p.tidk[i]]);if (t.arrival.empty()) continue;if (t.arrival.front() <= time) //表示在当前时间,确实有最近的请求没有客服处理,则该客服处理,队首请求出队{pti = time + t.t; //处理该主题耗时tneedtime = max(needtime, pti); //needtime用来表示处理完毕的时刻,每次都是在现有时间上,加上该主题的处理时间,如果比原来的timeneed大,则更新timeneed,这样可以保证,needtime时刻,所有人手头任务完成,所有请求处理完毕p.last = time;t.arrival.pop();if (t.arrival.empty()) n--; //某个主题的请求为空,则请求数自减break;}else if (t.arrival.front() < pti)pti = t.arrival.front();}p.next = pti; //更新当前的人的下一次状态变化临界时间}jumpt = min(pti, jumpt); //找到所有人的pti中的最小值,因为pti的最小值是整体状态变化的临界点(所谓状态变化,是指一个人从没处理主题到处理主题,或是从正在处理变为空闲),将可能存在状态变化的最小临界时间点(也就是所有人的pti中,最小的pti),作为新一轮循环的时间}time = jumpt;}cout << "Scenario " << ++kase <<": All requests are serviced within " << needtime << " minutes." << endl; } return 0; }



原创粉丝点击