首先题目里的每个人按优先级选工作的表达是不准确的,正确的说法是:让每个人都尽量选自己最熟悉的工作,并且如果一个人没找到自己最熟悉的工作的话,则要把挑选机会让给下一个人,而不是继续找自己下一个熟悉的工作.
举个例子
假定每个人的技能数都是5个,那么最坏情况下要经过5轮才能每个人都分配到工作,
也就是第一轮让每个人都尽量选自己最熟悉的工作,如果都找到了,则循环结束。否则没有找到工作的人进行第2轮挑选,
选的是自己第二熟悉的工作,依次类推.
给个实际数据,假定有两个业务员A和B,各自的技能列表为
A:{4 3 1 2}
B:{1 2 3}
那么,当有工作2要被分配的时候,最需要经过两轮挑选,最终分配给B
下面是测试数据
15
1 68 36 23 2
2 9 6 19 60
3 67 10 6 49
4 49 44 23 66
5 81 8 18 35
6 99 85 85 75
7 94 75 94 96
8 29 7 67 28
9 100 95 11 89
10 29 16 10 29
11 32 55 10 15
12 70 48 4 84
13 100 36 63 73
14 42 93 28 47
15 100 35 2 73
3
1 13 1 2 3 4 5 6 7 8 9 11 12 13 14
2 10 2 3 4 5 9 10 11 12 14 15
3 11 1 2 3 4 5 6 7 9 13 14 15
0
最终花费的时间为13899
下面是ac代码
#include <iostream>
#include <iomanip>
#include <cassert>
#include <list>
#include <cctype>
#include <climits>
#include <algorithm>
#include <map>
#include <set>
#include <queue>
#include <iterator>
#include <stack>
#include <string>
#include <vector>
#include <functional>
#include <utility>
#include <sstream>
#ifdef _DEBUG
#include <fstream>
#endif // _DEBUG
using namespace std;
#ifdef _DEBUG
ifstream ifs;
ofstream ofs;
const int UVA_PROBLEM_NO = 822;
void redirect_input()
{
ostringstream oss;
oss << "uva" << UVA_PROBLEM_NO << "_in.txt";
ifs.open(oss.str().c_str());
cin.rdbuf(ifs.rdbuf());
}
void redirect_output()
{
ostringstream oss;
oss << "uva" << UVA_PROBLEM_NO << "_out.txt";
ofs.open(oss.str().c_str());
cout.rdbuf(ofs.rdbuf());
}
#endif
void main_proc();
int main() {
#ifdef _DEBUG
redirect_input();
redirect_output();
#endif // _DEBUG
main_proc();
#ifdef _DEBUG
ifs.close();
ofs.close();
#endif
return 0;
}
struct Topic
{
int tid;
int num;
int t0;
int t;
int dt;
};
struct StaffInfo
{
int last_start_time; //每个人最后一个工作的开始时间
int last_end_time; //每个人最后一个工作的结束时间
vector<int> skills;
StaffInfo(const StaffInfo & other)
:last_end_time(other.last_end_time)
,last_start_time(other.last_start_time)
,skills(other.skills)
{
}
StaffInfo()
:
last_end_time(0)
,last_start_time(0)
{
}
};
typedef pair<int, int> Event; //记录每个事件发生的时间和事件编号
typedef pair<int, int> FreeStatff; //记录每个空闲的员工,第一坐标为上一个工作开始的时间,第二个为员工编号
struct Log {
int arrive; //事件到达的时间
int tid; //事件编号
int t0; //实际被处理的时间
int pid; //解决这个问题的人
Log(const Event& work, int t0, int pid)
:arrive(work.first)
, tid(work.second)
, t0(t0)
, pid(pid)
{
}
bool operator< (const Log& other)const
{
if (t0 != other.t0)
return t0 < other.t0;
else if (arrive != other.arrive)
return arrive < other.arrive;
else
return tid < other.tid;
}
Log(const Log& other) :
arrive(other.arrive)
, tid(other.tid)
, t0(other.t0)
, pid(other.pid)
{
}
Log& operator=(const Log& other) {
if (this != &other)
{
arrive = other.arrive;
tid = other.tid;
t0 = other.t0;
pid = other.pid;
}
return *this;
}
};
struct Scenario {
map<int, int> time_needed; //处理某个种类的请求所花的时间
set<Event> events; //记录每个事件发生的时间点和事件编号
map<int,StaffInfo> staffs;
int max_skill_num;
int last_end_time; //最晚结束的工作时间,也是最终要输出的值
set<int> time_points; //可能发生调度的时间点
set<Log> logs; //记录每个请求对应的人,用于调试
bool read()
{
bool flag = true;
if (flag=read_topics())
read_staffs();
return flag;
}
int get_process_time(int tid)const {
auto it = time_needed.find(tid);
assert(it != time_needed.end());
return it->second;
}
bool read_topics() {
int n;
cin >> n;
for (int i = 0;i < n;++i)
{
int tid = 0;
int num = 0;;
int t0 = 0;;
int t = 0;
int dt = 0;
cin >> tid >> num >> t0 >> t >> dt;
set<FreeStatff> empty_staffs;
for (int j = 0;j < num;++j, t0 += dt) {
time_needed[tid] = t;
Event event = make_pair(t0, tid);
events.insert(event);
time_points.insert(t0);
}
}
return n > 0;
}
void read_staffs()
{
max_skill_num = 0;
int n;
cin >> n;
for (int i = 0;i < n;++i) {
int pid;
cin >> pid;
int m;
cin >> m;
if (m>max_skill_num)
max_skill_num = m;
for (int j=0;j<m;++j)
{
int x;
cin >> x;
staffs[pid].skills.push_back(x);
}
}
}
//得到t0时刻在休息的职员列表
void get_wait_staffs(int t0, set<FreeStatff>& wait_queue) {
for (auto& e:staffs)
{
int tp = e.second.last_end_time;
if (tp <= t0)
wait_queue.insert(make_pair(e.second.last_start_time, e.first));
}
}
//得到t0时刻尚待处理的事件列表,it 指向这个列表的结尾
void get_wait_events(int t0,set<Event>& wait_event)
{
auto it = events.begin();
while (it != events.end())
{
int time = it->first;
if (time > t0)
break;
else {
wait_event.insert(*it);
it = events.erase(it);
}
}
}
void simulate(int t0)
{
#ifdef _DEBUG
//cout << "\nbegin at tp " << t0 << endl;
#endif // _DEBUG
set<Event> wait_events;
get_wait_events(t0, wait_events);
if (!wait_events.empty())
{
#ifdef _DEBUG
#if 0
cout << "wait_events: " << endl;
int n = 0;
for (auto e : wait_events)
{
cout << "(" << e.first << "," << e.second << ") ";
++n;
if (n % 10 == 0)
cout << endl;
}
cout << endl;
#endif
#endif // _DEBUG
set<FreeStatff > free_staffs;
get_wait_staffs(t0, free_staffs);
if (free_staffs.size() > 0) {
#ifdef _DEBUG
#if 0
cout << "free_staffs: " << endl;
int n = 0;
for (auto e : free_staffs)
{
cout << "(" << e.first << "," << e.second << ") ";
++n;
if (n % 10 == 0)
cout << endl;
}
cout << endl;
#endif
#endif // _DEBUG
allocate_work(t0, free_staffs, wait_events);
}
//把无人处理的事件再放回队列里
for (const auto & e : wait_events)
events.insert(e);
}
#ifdef _DEBUG
//cout << "end at tp " << t0 << endl <<endl;
#endif // _DEBUG
}
void simulate()
{
last_end_time = 0;
time_points.insert(0);
while (!events.empty())
{
auto it = time_points.begin();
int cur_time = *it;
time_points.erase(it);
simulate(cur_time);
}
cout << "All requests are serviced within " << last_end_time << " minutes." << endl;
}
StaffInfo& get_staff(int pid){
auto it = staffs.find(pid);
assert(it != staffs.end());
return it->second;
}
const StaffInfo& get_staff(int pid)const {
auto it = staffs.find(pid);
assert(it != staffs.end());
return it->second;
}
void allocate_work(int t0,set<FreeStatff>& free_staffs
, set<Event >& works) {
//注意选工作的时候轮数按最大技能数为准,也就是第一轮让每个人都先优先做其最熟悉的工作,
//如果第一轮结束后仍有未分配到工作的人,则让这些人再进来做其第二熟悉的工作,以此类推
for (size_t i=0;i<max_skill_num;++i)
{
//如果已经没人可用或者工作都被分配完了则直接结束
if (free_staffs.empty() || works.empty())
break;
auto it = free_staffs.begin();
for (auto it = free_staffs.begin()
; it != free_staffs.end()
;
)
{
int pid = it->second;
const StaffInfo& staff = get_staff(pid);
bool flag = false;
if (i<staff.skills.size()) //技能下标不能越界
{
int tid = staff.skills[i]; //在工作列表里找到第一个类型为tid的
for (auto it2 = works.begin()
;it2 != works.end()
;++it2
) {
if (it2->second == tid) {
allocate_work(t0, pid, *it2); //分配工作
works.erase(it2);
flag = true;
break;
}
}
}
if (flag) //当前技术员找到工作的话,则把它从空闲列表里删除
it = free_staffs.erase(it);
else
++it;
}
}
}
void allocate_work(int t0, int pid, const Event & work) {
StaffInfo& staff = get_staff(pid);
int tid = work.second;
int need_time = get_process_time(tid);
staff.last_start_time = t0;
staff.last_end_time = t0 + need_time;
time_points.insert(staff.last_end_time);
if (staff.last_end_time > last_end_time)
last_end_time = staff.last_end_time;
#ifdef _DEBUG
Log log(work,t0,pid);
logs.insert(log);
#if 0
cout << "topic " << tid << " arrived at time " << work.first
<< " is processed by " << pid << " at time " << t0
<< ",will ended at time " << staff.last_end_time << endl;
#endif
#endif // _DEBUG
}
void print()const {
for (auto& e:events)
cout << e.first << " " << e.second << endl;
for (auto& e : staffs) {
cout << e.first;
for (auto & f:e.second.skills)
cout << " " << f;
cout << endl;
}
}
void print_log()const
{
for (const auto& e : logs)
{
const int t = get_process_time(e.tid);
const int end = e.t0 + t;
cout << e.t0 << " " << e.tid
//<< " " << e.arrive
<< " " << e.pid
//<< " " << t
//<< " " << end
<<endl;
}
}
};
void main_proc() {
int case_no = 0;
while (true)
{
Scenario sce;
if (sce.read())
{
++case_no;
cout << "Scenario " << case_no << ": ";
//sce.print();
sce.simulate();
//sce.print_log();
}
else
break;
}
}
下面是部分中间日志,以便对拍.第一列为时间点,第二列为工作编号,第3列为职员编号,比如第2行7 8 1就表示在时刻7,将工作8分配给了职员1.
6 2 2
7 8 1
8 5 3
25 3 2
31 10 2
35 15 3