mirror of
https://github.com/kristoferssolo/customs.git
synced 2025-12-31 13:42:33 +00:00
Final version
This commit is contained in:
commit
594f5dc408
39
.gitignore
vendored
Normal file
39
.gitignore
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/target
|
||||||
|
#--------------------------------------------------#
|
||||||
|
# The following was generated with gitignore.nvim: #
|
||||||
|
#--------------------------------------------------#
|
||||||
|
# Gitignore for the following technologies: C++
|
||||||
|
|
||||||
|
# Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# Compiled Object files
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Compiled Dynamic libraries
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
|
||||||
|
# Fortran module files
|
||||||
|
*.mod
|
||||||
|
*.smod
|
||||||
|
|
||||||
|
# Compiled Static libraries
|
||||||
|
*.lai
|
||||||
|
*.la
|
||||||
|
*.a
|
||||||
|
*.lib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
/build
|
||||||
37
Makefile
Normal file
37
Makefile
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
CXX = g++
|
||||||
|
CXXFLAGS = -std=c++11 -Wall -Wextra -O3 -DNDEBUG
|
||||||
|
|
||||||
|
SRC_DIR = src
|
||||||
|
BUILD_DIR = build
|
||||||
|
BIN_DIR = $(BUILD_DIR)/bin
|
||||||
|
TEST_DIR = tests
|
||||||
|
|
||||||
|
SRC_FILES := $(wildcard $(SRC_DIR)/*.cc)
|
||||||
|
OBJ_FILES := $(patsubst $(SRC_DIR)/%.cc,$(BUILD_DIR)/%.o,$(SRC_FILES))
|
||||||
|
|
||||||
|
EXECUTABLE = $(BIN_DIR)/main
|
||||||
|
|
||||||
|
.PHONY: build clean run test
|
||||||
|
|
||||||
|
build: $(EXECUTABLE)
|
||||||
|
@echo "Build complete."
|
||||||
|
|
||||||
|
$(EXECUTABLE): $(OBJ_FILES)
|
||||||
|
@mkdir -p $(BIN_DIR)
|
||||||
|
$(CXX) $(CXXFLAGS) $^ -o $@
|
||||||
|
|
||||||
|
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cc
|
||||||
|
@mkdir -p $(BUILD_DIR)
|
||||||
|
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
|
||||||
|
run: $(EXECUTABLE)
|
||||||
|
./$(EXECUTABLE)
|
||||||
|
|
||||||
|
test:
|
||||||
|
@echo "Running tests..."
|
||||||
|
@cd $(TEST_DIR) && $(MAKE) test
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(BUILD_DIR) $(BIN_DIR)
|
||||||
|
@echo "Cleaned up."
|
||||||
2
data/input/customs.i1
Normal file
2
data/input/customs.i1
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
1 1 10 10
|
||||||
|
X
|
||||||
4
data/input/customs.i2
Normal file
4
data/input/customs.i2
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
1 1 10 10
|
||||||
|
P 1
|
||||||
|
N 2
|
||||||
|
X
|
||||||
4
data/input/customs.i3
Normal file
4
data/input/customs.i3
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
1 1 10 10
|
||||||
|
P 1
|
||||||
|
P 2
|
||||||
|
X
|
||||||
13
data/input/customs.i4
Normal file
13
data/input/customs.i4
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
2 1 10 10
|
||||||
|
T P 2 21
|
||||||
|
P 1
|
||||||
|
P 2
|
||||||
|
P 3
|
||||||
|
P 4
|
||||||
|
P 5
|
||||||
|
P 6
|
||||||
|
P 7
|
||||||
|
P 8
|
||||||
|
P 9
|
||||||
|
P 10
|
||||||
|
X
|
||||||
13
data/input/customs.i5
Normal file
13
data/input/customs.i5
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
1 5 10 10
|
||||||
|
T N 1 5
|
||||||
|
T N 3 3
|
||||||
|
T N 5 1
|
||||||
|
T N 4 2
|
||||||
|
T N 2 5
|
||||||
|
N 997
|
||||||
|
N 1001
|
||||||
|
N 1002
|
||||||
|
N 1003
|
||||||
|
N 1004
|
||||||
|
N 1005
|
||||||
|
X
|
||||||
14
data/input/customs.i6
Normal file
14
data/input/customs.i6
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
2 3 10 50
|
||||||
|
T P 1 7
|
||||||
|
T N 2 80
|
||||||
|
P 1
|
||||||
|
N 2
|
||||||
|
N 10
|
||||||
|
N 20
|
||||||
|
N 30
|
||||||
|
N 40
|
||||||
|
P 45
|
||||||
|
P 50
|
||||||
|
P 53
|
||||||
|
N 60
|
||||||
|
X
|
||||||
2004
data/input/customs.i7
Normal file
2004
data/input/customs.i7
Normal file
File diff suppressed because it is too large
Load Diff
1
data/output/customs.o1
Normal file
1
data/output/customs.o1
Normal file
@ -0,0 +1 @@
|
|||||||
|
nothing
|
||||||
2
data/output/customs.o2
Normal file
2
data/output/customs.o2
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
1 11
|
||||||
|
2 12
|
||||||
2
data/output/customs.o3
Normal file
2
data/output/customs.o3
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
1 11
|
||||||
|
2 21
|
||||||
10
data/output/customs.o4
Normal file
10
data/output/customs.o4
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
1 11
|
||||||
|
3 21
|
||||||
|
2 23
|
||||||
|
4 31
|
||||||
|
6 41
|
||||||
|
5 44
|
||||||
|
7 51
|
||||||
|
9 61
|
||||||
|
8 65
|
||||||
|
10 71
|
||||||
6
data/output/customs.o5
Normal file
6
data/output/customs.o5
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
997 1002
|
||||||
|
1001 1006
|
||||||
|
1003 1006
|
||||||
|
1004 1006
|
||||||
|
1005 1006
|
||||||
|
1002 1007
|
||||||
10
data/output/customs.o6
Normal file
10
data/output/customs.o6
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
1 8
|
||||||
|
45 52
|
||||||
|
2 52
|
||||||
|
53 60
|
||||||
|
50 60
|
||||||
|
20 70
|
||||||
|
10 90
|
||||||
|
30 102
|
||||||
|
40 120
|
||||||
|
60 170
|
||||||
2000
data/output/customs.o7
Normal file
2000
data/output/customs.o7
Normal file
File diff suppressed because it is too large
Load Diff
679
src/main.cc
Normal file
679
src/main.cc
Normal file
@ -0,0 +1,679 @@
|
|||||||
|
#include <cstdint>
|
||||||
|
#include <stdexcept>
|
||||||
|
constexpr char FILENAME_IN[] = "customs.in";
|
||||||
|
constexpr char FILENAME_OUT[] = "customs.out";
|
||||||
|
constexpr uint8_t LINE_LENGTH = 20;
|
||||||
|
using Time = uint32_t;
|
||||||
|
using CustomsCount = uint8_t;
|
||||||
|
|
||||||
|
template <typename T> void swap(T *a, T *b) {
|
||||||
|
T temp = *a;
|
||||||
|
*a = *b;
|
||||||
|
*b = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> struct Vec {
|
||||||
|
public:
|
||||||
|
void push(const T &value) {
|
||||||
|
if (m_size == m_capacity) {
|
||||||
|
reserve_exact(m_size == 0 ? 1 : 2 * m_size);
|
||||||
|
}
|
||||||
|
data[m_size++] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
T pop() {
|
||||||
|
if (is_empty()) {
|
||||||
|
throw std::out_of_range("Cannot pop from empty Vec");
|
||||||
|
}
|
||||||
|
return data[m_size--];
|
||||||
|
}
|
||||||
|
|
||||||
|
T first() {
|
||||||
|
if (is_empty()) {
|
||||||
|
throw std::out_of_range("Cannot get first from empty Vec");
|
||||||
|
}
|
||||||
|
return data[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
T last() {
|
||||||
|
if (is_empty()) {
|
||||||
|
throw std::out_of_range("Cannot get last from empty Vec");
|
||||||
|
}
|
||||||
|
return data[m_size - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t len() const {
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t capacity() const {
|
||||||
|
return m_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_empty() const {
|
||||||
|
return m_size == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
m_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool contains(const T &value) const {
|
||||||
|
for (size_t i = 0; i < m_size; ++i) {
|
||||||
|
if (data[i] == value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
T first() const {
|
||||||
|
if (is_empty()) {
|
||||||
|
throw std::out_of_range("Cannot get first from empty Vec");
|
||||||
|
}
|
||||||
|
return data[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
T &operator[](const size_t idx) {
|
||||||
|
if (idx >= m_size) {
|
||||||
|
throw std::out_of_range("Index out of range");
|
||||||
|
}
|
||||||
|
return data[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
const T &operator[](const size_t idx) const {
|
||||||
|
if (idx >= m_size) {
|
||||||
|
throw std::out_of_range("Index out of range");
|
||||||
|
}
|
||||||
|
return data[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
T get(const size_t idx) const {
|
||||||
|
if (idx >= m_size) {
|
||||||
|
throw std::out_of_range("Index out of range");
|
||||||
|
}
|
||||||
|
return data[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec<T> reserve_exact(size_t additional) {
|
||||||
|
if (additional > m_capacity) {
|
||||||
|
T *new_data = new T[additional];
|
||||||
|
for (size_t i = 0; i < m_size; ++i) {
|
||||||
|
new_data[i] = data[i];
|
||||||
|
}
|
||||||
|
delete[] data;
|
||||||
|
data = new_data;
|
||||||
|
m_capacity = additional;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Vec<T> with_capacity(size_t capacity) {
|
||||||
|
auto vec = Vec<T>::create();
|
||||||
|
vec.reserve_exact(capacity);
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Vec<T> create() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
class Iterator {
|
||||||
|
public:
|
||||||
|
explicit Iterator(T *ptr): ptr(ptr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator &operator++() {
|
||||||
|
++ptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const Iterator &other) const {
|
||||||
|
return ptr != other.ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
T &operator*() const {
|
||||||
|
return *ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T *ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
Iterator begin() const {
|
||||||
|
return Iterator(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator end() const {
|
||||||
|
return Iterator(data + m_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec(): data(nullptr), m_capacity(0), m_size(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
T *data;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
size_t m_capacity;
|
||||||
|
size_t m_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct BinaryHeap {
|
||||||
|
BinaryHeap(): data(Vec<T>::create()) {
|
||||||
|
data.push(T());
|
||||||
|
}
|
||||||
|
|
||||||
|
static BinaryHeap<T> create() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(const T &value) {
|
||||||
|
data.push(value);
|
||||||
|
sift_up(len());
|
||||||
|
}
|
||||||
|
|
||||||
|
T pop() {
|
||||||
|
if (is_empty()) {
|
||||||
|
throw std::out_of_range("Cannot pop from empty BinaryHeap");
|
||||||
|
}
|
||||||
|
T first = data[1];
|
||||||
|
|
||||||
|
swap(&data[len()], &data[1]);
|
||||||
|
data.pop();
|
||||||
|
|
||||||
|
sift_down(1);
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *peek() {
|
||||||
|
return &data[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
data.clear();
|
||||||
|
data.push(T());
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec<T> *into_vec() {
|
||||||
|
return &data;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t len() const {
|
||||||
|
return data.len() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t capacity() const {
|
||||||
|
return data.capacity() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_empty() const {
|
||||||
|
return len() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BinaryHeap<T> from(const Vec<T> &data) {
|
||||||
|
BinaryHeap<T> heap = BinaryHeap<T>::create();
|
||||||
|
for (const auto &value : data) {
|
||||||
|
heap.push(value);
|
||||||
|
}
|
||||||
|
return heap;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BinaryHeap<T> with_capacity(const size_t capacity) {
|
||||||
|
BinaryHeap<T> heap = BinaryHeap<T>::create();
|
||||||
|
heap.data = Vec<T>::with_capacity(capacity);
|
||||||
|
heap.push(T());
|
||||||
|
return heap;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Iterator {
|
||||||
|
public:
|
||||||
|
explicit Iterator(T *ptr): ptr(ptr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator &operator++() {
|
||||||
|
++ptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const Iterator &other) const {
|
||||||
|
return ptr != other.ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
T &operator*() const {
|
||||||
|
return *ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T *ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
Iterator begin() const {
|
||||||
|
return Iterator(data.data + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator end() const {
|
||||||
|
return Iterator(data.data + data.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
T &operator[](const size_t idx) {
|
||||||
|
if (idx >= len()) {
|
||||||
|
throw std::out_of_range("Index out of range");
|
||||||
|
}
|
||||||
|
return data[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
const T &operator[](const size_t idx) const {
|
||||||
|
if (idx >= len()) {
|
||||||
|
throw std::out_of_range("Index out of range");
|
||||||
|
}
|
||||||
|
return data[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
void sift_up(size_t idx) {
|
||||||
|
if (idx <= 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t parent_idx = idx / 2;
|
||||||
|
if (data[idx] < data[parent_idx]) {
|
||||||
|
swap(&data[idx], &data[parent_idx]);
|
||||||
|
sift_up(parent_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sift_down(size_t idx) {
|
||||||
|
size_t smallest = idx;
|
||||||
|
size_t left_child = idx * 2;
|
||||||
|
size_t right_child = idx * 2 + 1;
|
||||||
|
size_t size = data.len();
|
||||||
|
|
||||||
|
if (left_child < size && data[left_child] < data[smallest]) {
|
||||||
|
smallest = left_child;
|
||||||
|
}
|
||||||
|
if (right_child < size && data[right_child] < data[smallest]) {
|
||||||
|
smallest = right_child;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (smallest != idx) {
|
||||||
|
swap(&data[idx], &data[smallest]);
|
||||||
|
sift_down(smallest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Vec<T> data;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Citizenship {
|
||||||
|
Citizen,
|
||||||
|
NonCitizen,
|
||||||
|
Undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace citizenshipType {
|
||||||
|
Citizenship from_str(const char &ch) {
|
||||||
|
switch (ch) {
|
||||||
|
case 'P': return Citizenship::Citizen;
|
||||||
|
case 'N': return Citizenship::NonCitizen;
|
||||||
|
default: return Citizenship::Undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *to_string(const Citizenship type) {
|
||||||
|
switch (type) {
|
||||||
|
case Citizenship::Citizen: return "Citizen";
|
||||||
|
case Citizenship::NonCitizen: return "NonCitizen";
|
||||||
|
case Citizenship::Undefined: return "Undefined";
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
} // namespace citizenshipType
|
||||||
|
|
||||||
|
struct Traveler {
|
||||||
|
Time arrival_time;
|
||||||
|
Citizenship citizenship;
|
||||||
|
Time exit_time = 0;
|
||||||
|
CustomsCount officer_id = 0;
|
||||||
|
|
||||||
|
Traveler(): arrival_time(0), citizenship(Citizenship::Undefined) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static Traveler create(Time arrival_time, Citizenship type) {
|
||||||
|
return {arrival_time, type};
|
||||||
|
}
|
||||||
|
|
||||||
|
static Traveler from_str(const char *string) {
|
||||||
|
char t;
|
||||||
|
Time arrival_time;
|
||||||
|
sscanf(string, "%s %u", &t, &arrival_time);
|
||||||
|
Citizenship type = citizenshipType::from_str(t);
|
||||||
|
return {arrival_time, type};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Traveler &other) const {
|
||||||
|
return arrival_time == other.arrival_time && citizenship == other.citizenship && exit_time == other.exit_time &&
|
||||||
|
officer_id == other.officer_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const Traveler &other) const {
|
||||||
|
if (exit_time != other.exit_time) {
|
||||||
|
return exit_time < other.exit_time;
|
||||||
|
}
|
||||||
|
if (citizenship != other.citizenship) {
|
||||||
|
return citizenship < other.citizenship;
|
||||||
|
}
|
||||||
|
return officer_id < other.officer_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const Traveler &other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>(const Traveler &other) {
|
||||||
|
return other < *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<=(const Traveler &other) const {
|
||||||
|
return !(other < *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>=(const Traveler &other) const {
|
||||||
|
return !(*this < other);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_wait_time_exceede(Time exit_time, Time max_wait_time) {
|
||||||
|
return (exit_time - this->exit_time) > max_wait_time + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print() {
|
||||||
|
printf("%d %d\n", arrival_time, exit_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Traveler(Time arrival_time, Citizenship type): arrival_time(arrival_time), citizenship(type) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Officer {
|
||||||
|
CustomsCount id;
|
||||||
|
Time processing_time;
|
||||||
|
Time departure_time = 1;
|
||||||
|
Citizenship citizenship;
|
||||||
|
|
||||||
|
Officer(): id(0), processing_time(0), citizenship(Citizenship::Undefined) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static Officer create(CustomsCount id, Time processing_time, Citizenship citizenship) {
|
||||||
|
return {id, processing_time, citizenship};
|
||||||
|
}
|
||||||
|
|
||||||
|
Time total_time() const {
|
||||||
|
return processing_time + departure_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Officer from_str(const char *string) {
|
||||||
|
char t, type;
|
||||||
|
CustomsCount customs_id;
|
||||||
|
Time processing_time;
|
||||||
|
|
||||||
|
sscanf(string, "%s %s %hhu %u", &t, &type, &customs_id, &processing_time);
|
||||||
|
|
||||||
|
Citizenship citizenship = citizenshipType::from_str(type);
|
||||||
|
return {customs_id, processing_time, citizenship};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Officer &other) const {
|
||||||
|
return id == other.id && processing_time == other.processing_time && departure_time == other.departure_time &&
|
||||||
|
citizenship == other.citizenship;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const Officer &other) const {
|
||||||
|
if (departure_time != other.departure_time) {
|
||||||
|
return departure_time < other.departure_time;
|
||||||
|
}
|
||||||
|
return id < other.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const Officer &other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>(const Officer &other) {
|
||||||
|
return other < *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<=(const Officer &other) const {
|
||||||
|
return !(other < *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>=(const Officer &other) const {
|
||||||
|
return !(*this < other);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Officer(CustomsCount id, Time processing_time, Citizenship citizenship):
|
||||||
|
id(id),
|
||||||
|
processing_time(processing_time),
|
||||||
|
citizenship(citizenship) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DefaultOfficer {
|
||||||
|
CustomsCount count;
|
||||||
|
Time time;
|
||||||
|
Citizenship citizenship;
|
||||||
|
|
||||||
|
static DefaultOfficer create(const CustomsCount count, const Time time, const Citizenship citizenship) {
|
||||||
|
return {count, time, citizenship};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
DefaultOfficer(const CustomsCount count, const Time time, const Citizenship citizenship):
|
||||||
|
count(count),
|
||||||
|
time(time),
|
||||||
|
citizenship(citizenship) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CustomsInfo {
|
||||||
|
DefaultOfficer citizen;
|
||||||
|
DefaultOfficer non_citizen;
|
||||||
|
|
||||||
|
static CustomsInfo from_str(const char *string) {
|
||||||
|
Time citizen_time, non_citizen_time;
|
||||||
|
CustomsCount citizen_count, non_citizen_count;
|
||||||
|
|
||||||
|
sscanf(string, "%hhu %hhu %u %u", &citizen_count, &non_citizen_count, &citizen_time, &non_citizen_time);
|
||||||
|
|
||||||
|
return {DefaultOfficer::create(citizen_count, citizen_time, Citizenship::Citizen),
|
||||||
|
DefaultOfficer::create(non_citizen_count, non_citizen_time, Citizenship::NonCitizen)};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CustomsInfo(const DefaultOfficer &citizen, const DefaultOfficer &non_citizen):
|
||||||
|
citizen(citizen),
|
||||||
|
non_citizen(non_citizen) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CustomsVec {
|
||||||
|
Vec<Officer> citizens;
|
||||||
|
Vec<Officer> non_citizens;
|
||||||
|
|
||||||
|
static CustomsVec from(const CustomsInfo &value) {
|
||||||
|
auto self = CustomsVec::create();
|
||||||
|
self.citizens = Vec<Officer>::with_capacity(value.citizen.count);
|
||||||
|
self.set_default_times(value.citizen, Citizenship::Citizen);
|
||||||
|
|
||||||
|
self.non_citizens = Vec<Officer>::with_capacity(value.non_citizen.count);
|
||||||
|
self.set_default_times(value.non_citizen, Citizenship::NonCitizen);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(const Officer &officer) {
|
||||||
|
switch (officer.citizenship) {
|
||||||
|
case Citizenship::Citizen: citizens.push(officer); break;
|
||||||
|
case Citizenship::NonCitizen: non_citizens.push(officer); break;
|
||||||
|
case Citizenship::Undefined: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_empty() const {
|
||||||
|
return citizens.is_empty() && non_citizens.is_empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void replace(const Officer &officer) {
|
||||||
|
switch (officer.citizenship) {
|
||||||
|
case Citizenship::Citizen: citizens[officer.id - 1] = officer; break;
|
||||||
|
case Citizenship::NonCitizen: non_citizens[officer.id - 1] = officer; break;
|
||||||
|
case Citizenship::Undefined: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_default_times(const DefaultOfficer &default_officer, const Citizenship citizenship) {
|
||||||
|
for (CustomsCount customs_id = 1; customs_id <= default_officer.count; ++customs_id) {
|
||||||
|
push(Officer::create(customs_id, default_officer.time, citizenship));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static CustomsVec create() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CustomsVec(): citizens(Vec<Officer>::create()), non_citizens(Vec<Officer>::create()) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CustomsBinaryHeap {
|
||||||
|
BinaryHeap<Officer> citizens;
|
||||||
|
BinaryHeap<Officer> non_citizens;
|
||||||
|
|
||||||
|
static CustomsBinaryHeap create() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(Officer officer) {
|
||||||
|
switch (officer.citizenship) {
|
||||||
|
case Citizenship::Citizen: citizens.push(officer); break;
|
||||||
|
case Citizenship::NonCitizen: non_citizens.push(officer); break;
|
||||||
|
case Citizenship::Undefined: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_empty() const {
|
||||||
|
return citizens.is_empty() && non_citizens.is_empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void process(Traveler *traveler) {
|
||||||
|
BinaryHeap<Officer> customs_heap;
|
||||||
|
switch (traveler->citizenship) {
|
||||||
|
case Citizenship::Citizen: customs_heap = citizens; break;
|
||||||
|
case Citizenship::NonCitizen: customs_heap = non_citizens; break;
|
||||||
|
case Citizenship::Undefined: throw std::logic_error("Unexpected citizen type");
|
||||||
|
}
|
||||||
|
if (customs_heap.is_empty()) {
|
||||||
|
throw std::logic_error("Unexpected empty heap");
|
||||||
|
}
|
||||||
|
Officer officer = customs_heap.pop();
|
||||||
|
traveler->exit_time = officer.total_time();
|
||||||
|
officer.departure_time = officer.total_time();
|
||||||
|
traveler->officer_id = officer.id;
|
||||||
|
customs_heap.push(officer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static CustomsBinaryHeap from(const CustomsVec &data) {
|
||||||
|
auto self = CustomsBinaryHeap::create();
|
||||||
|
self.citizens = BinaryHeap<Officer>::from(data.citizens);
|
||||||
|
self.non_citizens = BinaryHeap<Officer>::from(data.non_citizens);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_departure_times(const Traveler &traveler) {
|
||||||
|
Time time = traveler.arrival_time;
|
||||||
|
size_t idx = 1;
|
||||||
|
BinaryHeap<Officer> customs;
|
||||||
|
switch (traveler.citizenship) {
|
||||||
|
case Citizenship::Citizen: customs = citizens; break;
|
||||||
|
case Citizenship::NonCitizen: customs = non_citizens; break;
|
||||||
|
case Citizenship::Undefined: return;
|
||||||
|
}
|
||||||
|
for (auto &officer : customs) {
|
||||||
|
if (officer.departure_time < time) {
|
||||||
|
officer.departure_time = time;
|
||||||
|
customs.sift_up(idx);
|
||||||
|
++idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CustomsBinaryHeap(): citizens(BinaryHeap<Officer>::create()), non_citizens(BinaryHeap<Officer>::create()) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
char *get_line(char *string) {
|
||||||
|
return fgets(string, LINE_LENGTH, stdin);
|
||||||
|
}
|
||||||
|
|
||||||
|
Time max_time(const Time a, const Time b) {
|
||||||
|
return (a > b) ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parse_input() {
|
||||||
|
char *line = new char[LINE_LENGTH];
|
||||||
|
CustomsInfo first_line = CustomsInfo::from_str(get_line(line));
|
||||||
|
|
||||||
|
auto customs_vec = CustomsVec::from(first_line);
|
||||||
|
|
||||||
|
size_t max_wait_time = max_time(first_line.citizen.time, first_line.non_citizen.time);
|
||||||
|
|
||||||
|
bool output_exists = false;
|
||||||
|
|
||||||
|
while (get_line(line)[0] != 'X') {
|
||||||
|
if (line[0] != 'T') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Replace officers with non default values
|
||||||
|
Officer officer = Officer::from_str(line);
|
||||||
|
customs_vec.replace(officer);
|
||||||
|
max_wait_time = max_time(max_wait_time, officer.processing_time);
|
||||||
|
}
|
||||||
|
auto customs_heap = CustomsBinaryHeap::from(customs_vec);
|
||||||
|
auto travelers_heap = BinaryHeap<Traveler>::with_capacity(10);
|
||||||
|
|
||||||
|
for (; line[0] != 'X'; get_line(line)) {
|
||||||
|
Traveler traveler = Traveler::from_str(line);
|
||||||
|
customs_heap.update_departure_times(traveler);
|
||||||
|
customs_heap.process(&traveler);
|
||||||
|
travelers_heap.push(traveler);
|
||||||
|
|
||||||
|
if (travelers_heap.peek()->is_wait_time_exceede(traveler.exit_time, max_wait_time)) {
|
||||||
|
travelers_heap.pop().print();
|
||||||
|
output_exists = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (travelers_heap.len() > 0) {
|
||||||
|
travelers_heap.pop().print();
|
||||||
|
output_exists = true;
|
||||||
|
}
|
||||||
|
delete[] line;
|
||||||
|
return output_exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
void process() {
|
||||||
|
if (!parse_input()) {
|
||||||
|
printf("nothing\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
freopen(FILENAME_IN, "r", stdin);
|
||||||
|
freopen(FILENAME_OUT, "w", stdout);
|
||||||
|
|
||||||
|
process();
|
||||||
|
|
||||||
|
fclose(stdin);
|
||||||
|
fclose(stdout);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user