#include #include const int n = 10; const int n5 = 6; const int n6 = 3; const int nT = 50; int f1() { return random(4) + 2; } int f2() { return random(10); } template class TQueue { public: TQueue() { front = back = 0; } ~TQueue(); T get(); void put(const T&); int is_empty() { return((!front) ? 1 : 0); } private: class TQueueItem { public: TQueueItem(T value): item(value), next(0) { } T item; TQueueItem *next; }; TQueueItem *front, *back; }; template TQueue :: ~TQueue() { while(!is_empty()) (void)get(); } template void TQueue :: put(const T &value) { TQueueItem *pt = new TQueueItem(value); if(is_empty()) { front = back = pt; } else { back -> next = pt; back = pt; } } template T TQueue :: get() { if(is_empty()) { cout << "Trying to Get from the empty queue !\n"; exit(-1); } TQueueItem *pt = front; front = front -> next; T value = pt -> item; delete pt; return value; } class TClient { int ID; public: int get_id() const { return ID; } TClient(int value): ID(value) { } }; enum status { // состояние "занят: работаю с клиентом" _busy = 0, // состояние: "свободен - жду очередного клиента" _waiting, // состояние: "отдыхаю" _vacation }; class TCasher { status state; const int work_time, ID; int working_time, this_client; public: TCasher(int _id, int _work): ID(_id), work_time(_work), state(_waiting) { working_time = work_time; } // Функция Handle вызывается ежеминутно из "менеджера", и возвращает текущее // состояние кассира (как результат) и через параметр changed - признак того, что состояние изменилось status Handle(int &changed) { changed = 0; // Если кассир работал с клиентом, то уменьшаем оставшееся время работы с ним, // и одновременно уменьшаем счетчик времени до очередного отдыха кассира if(state == _busy) { this_client -= 1; working_time -= 1; // Если this_client = 0, это значит, что с этим клиентом мы закончили работать, статус изменился // как минимум на "ожидание", кроме этого - устанавливаем признак того, что состояние изменилось if(!this_client) state = _waiting, changed = 1; } // Если кассир отдыхал - то уменьшаем время отдыха (когда кассир начинает отдыхать, в переменную // working_time записывается _отрицательное_ значение, и постепенно увеличивается, чтобы не вводить // специальную переменную) if(state == _vacation) { working_time += 1; // Если working_time = 0, то отдых закончился, и переходим в "ожидание", с установкой признака изменения // состояния, и кроме этого, устанавливаем положительное время работы ДО следующего отдыха if(working_time > 0) state = _waiting, working_time = work_time, changed = 1; } // Если после всех предыдущих проверок состояние = "ожидание" ... if(state == _waiting) { // ... если не было изменения состояния - уменьшаем время до очередного отдыха // (если это условие не поставить, то время может уменьшится дважды - один раз при // переходе от _busy к _waiting, и второй раз - здесь, а это делать нежелательно) if(!changed) working_time -= 1; // ... и, наконец, проверяем не пришло ли время отдохнуть (это уже проверяется независимо // от того, было ли изменение состояния, т.е. если кассир на этой минуте закончил работать с // клиентом, и ему уже пора отдыхать, он должен начать отдыхать...), и если пришло, то ставим // отрицательное время работы равное константе N6, и сигнализируем об изменении состояния if(working_time <= 0) state = _vacation, working_time = -n6, changed = 1; } return state; } // Эта функция просто напросто имитирует подход клиента к кассиру: переводит кассира в // состояние "занят" и устанавливает время обслуживания данного клиента... void next() { state = _busy; this_client = f2(); } }; // Основной управляющий класс - "менеджер"... class TManager { // Включает в себя очередь "посетителей", массив "кассиров" ... TQueue queue; TCasher *cash_workers[n]; // ... текущее время (0 - при начале работы банка), и время после прихода последнего посетителя int current_time, after_prev_enter; public: TManager(); ~TManager(); void run(); }; TManager :: TManager(): current_time(0), after_prev_enter(0) { // инициализируем "кассиров" for(int i = 0; i < n; i++) cash_workers[i] = new TCasher(i + 1, n5); } // в дестркуторе - "закрываем кассы" TManager :: ~TManager() { for(int i = 0; i < n; i++) delete cash_workers[i]; } // вот в этой функции как раз и происходит вся имитация: void TManager :: run() { // эта переменная - для хранения количества пришедших посетителей // (используется для присвоения ID клиенту) static int client_entered = 0; // Все операции продолжаются до тех пор, пока не вышло время работы банка, // и потом, пока в очереди есть хотя бы один посетитель (не выгонять же, правда?) while((current_time <= nT) || (!queue.is_empty())) { // Перебинаем всех кассиров, и для каждого ... for(int i = 0; i < n; i++) { // предполагаем, что его статус НЕ изменился int status_changed = 0; // получаем статус кассира обращением к ЕГО методу status curr_status = cash_workers[i] -> Handle(status_changed); // если статус изменился - отобразить этот факт в статистике (какой кассир, как изменился статус, // когда изменился - в какое время) if(status_changed) // statistics switch(curr_status) { case _vacation: cout << "casher #" << i << " goes to rest at:: " << current_time << endl; break; case _waiting: cout << "casher #" << i << " awaiting at:: " << current_time << endl; break; } // если сейчас проверяемый кассир ожидает клиента, то ... if(curr_status == _waiting) if(!queue.is_empty()) { // ... вытянуть посетителя из очереди (если там кто-то есть, разумеется) TClient client = queue.get(); // добавить в статистику, что такой-то посетитель направлен к такому-то кассиру в такое-то время cout << "client #" << client.get_id() << " sent to casher #" << i << " at:: " << current_time << endl; // и, собственно, направить клиента к кассиру cash_workers[i] -> next(); } } // здесь (после каждого прохода по всем кассирам) проверяем, не закончилось ли время работы банка ... if(current_time <= nT) { // если еще НЕ закончилось - то увеличиваем время, прошедшее после прихода последнего посетителя after_prev_enter += 1; // и если это время больше того, которое должно быть по формуле (интервал между приходом посетителей) if(after_prev_enter > f1()) { // то имитируем приход очередного клиента: он только что пришел, т.е. время обнуляем ... after_prev_enter = 0; // вызываем конструктор TClient, перед этим увеличиваем общий счетчик посетителей, и передаем // его значение как ID в конструктор (чтобы ВСЕ посетители хоть чем-то различались) TClient client(++client_entered); // добавляем вновь прибывшего посетителя в очередь queue.put(client); } // если же время работы банка уже закончилось, то новые посетители НЕ будут добавляться в очередь, // а те, кто пришел раньше, и уже стоит в очереди, будут обслужены, и только тогда этот цикл закончится... } // прибавляем к "банковскому" времени минуту, и начинаем заново опрашивать кассиров current_time += 1; } } int main() { TManager bank; bank.run(); return 0; }