Редакция для Ранжирование резюме
Submitting an official solution before solving the problem yourself is a bannable offence.
Автор:
Ранжирование резюме
1. Идея
Нужно обработать каждого кандидата независимо:
- проверить, проходит ли он первичный фильтр;
- если проходит — посчитать его
rating; - сохранить кандидата для последующей сортировки.
После этого остаётся отсортировать всех прошедших кандидатов:
- по убыванию
rating; - при равенстве рейтинга — по имени в лексикографическом порядке.
Если никто не прошёл, выводим NO CANDIDATES.
2. Наблюдения
Наблюдение 1. Проверка обязательных навыков
У вакансии есть множество обязательных навыков required, а у кандидата — множество его навыков.
Кандидат проходит по навыкам тогда и только тогда, когда каждый навык из required содержится у кандидата.
Для такой проверки удобно хранить навыки кандидата в set:
- в C++ —
set<string>; - в Python —
set.
Тогда проверка каждого обязательного навыка выполняется быстро и просто.
Наблюдение 2. Что такое matched_optional
По условию:
matched_optional — это количество навыков кандидата, которые не входят в required.
То есть среди всех навыков кандидата нужно посчитать те, которых нет в обязательном списке.
Так как у кандидата навыки различны, достаточно просто пройти по его списку навыков и увеличить счётчик для каждого навыка, не принадлежащего required.
Наблюдение 3. Как удобно сортировать
Нужно сортировать по убыванию rating, а имя — по возрастанию.
Удобный приём: сохранять пару (-rating, name).
Тогда обычная сортировка по возрастанию даст нужный порядок:
- больший
ratingпревращается в меньший-rating, значит окажется раньше; - при равенстве первого элемента пары имена будут отсортированы по возрастанию автоматически.
Наблюдение 4. Пустая строка во входе
Если r = 0, строка с обязательными навыками всё равно присутствует, но она пустая.
Аналогично, если s = 0, строка навыков кандидата тоже пустая.
Эталонные решения читают эти строки через разбиение на слова:
- пустая строка превращается в пустой список;
- это как раз то, что нужно.
3. Алгоритм
- Считать
min_exp,min_score,max_salary,r. - Считать список обязательных навыков и сохранить его в множество
required. - Считать число кандидатов
n. Завести список
passedдля кандидатов, прошедших фильтр.Для каждого кандидата:
- считать
name,exp,score,salary,s; - считать список его навыков
skills; - построить множество
skill_set; - проверить базовые условия:
exp >= min_exp;score >= min_score;salary <= max_salary;
- если они выполнены, проверить наличие всех навыков из
requiredвskill_set; - если кандидат прошёл:
- посчитать
matched_optional— число навыков изskills, которых нет вrequired; - вычислить
rating = score * 10 + matched_optional * 5 + exp * 2 - salary // 10000; - сохранить пару
(-rating, name)вpassed.
- посчитать
- считать
Отсортировать
passed.- Если
passedпуст:- вывести
NO CANDIDATES.
- вывести
- Иначе вывести имена кандидатов по порядку.
4. Почему это работает
Докажем, что алгоритм выводит правильный ответ.
1. Фильтрация выполняется точно по условию
Для каждого кандидата алгоритм отдельно проверяет:
- достаточен ли опыт;
- достаточен ли балл;
- не слишком ли велика зарплата;
- содержатся ли все обязательные навыки среди навыков кандидата.
Это в точности совпадает с определением первичного фильтра из условия.
Значит, кандидат попадает в список passed тогда и только тогда, когда действительно проходит фильтр.
2. matched_optional считается правильно
Алгоритм проходит по всем навыкам кандидата и считает те, которые не входят в required.
По условию именно это и требуется: количество навыков кандидата, не являющихся обязательными.
Следовательно, значение matched_optional вычисляется верно.
3. rating считается по заданной формуле
Для каждого прошедшего кандидата алгоритм вычисляет:
score * 10 + matched_optional * 5 + exp * 2 - salary // 10000
Это полностью совпадает с формулой из условия.
Значит, рейтинг каждого кандидата найден правильно.
4. Сортировка даёт нужный порядок
В список сохраняется пара (-rating, name).
При стандартной сортировке пар:
- сначала сравнивается
-rating, поэтому кандидаты с большим рейтингом идут раньше; - если рейтинги равны, сравниваются имена, поэтому раньше идёт лексикографически меньшее имя.
Это ровно тот порядок, который требуется в задаче.
Из пунктов 1–4 следует, что алгоритм выводит всех и только тех кандидатов, которые должны быть выведены, и в правильном порядке.
5. Сложность
Пусть:
n— число кандидатов;r— число обязательных навыков;s— число навыков у текущего кандидата.
Для одного кандидата:
- чтение и построение множества навыков:
O(s log s)дляsetв C++ и в среднемO(s)дляsetв Python; - проверка всех обязательных навыков:
O(r log s)в C++ и в среднемO(r)в Python; - подсчёт
matched_optional:O(s log r)в C++ и в среднемO(s)в Python.
Так как по условию r <= 20 и s <= 20, это очень мало, и решение легко укладывается в ограничения.
Сортировка прошедших кандидатов занимает O(k log k), где k — количество прошедших кандидатов, k <= n.
Итоговая сложность:
- в C++:
O(n * (r + s) * log 20 + k log k), что на практике фактическиO(n + k log k); - в Python: в среднем
O(n * (r + s) + k log k).
По памяти:
O(k)на хранение прошедших кандидатов.
6. Код на C++17
#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <algorithm>
using namespace std;
int main() {
int min_exp, min_score, r;
long long max_salary;
cin >> min_exp >> min_score >> max_salary >> r;
set<string> required;
for (int i = 0; i < r; i++) {
string skill;
cin >> skill;
required.insert(skill);
}
int n;
cin >> n;
vector<pair<long long, string>> passed;
for (int i = 0; i < n; i++) {
string name;
int exp, score, s;
long long salary;
cin >> name >> exp >> score >> salary >> s;
vector<string> skills(s);
set<string> skill_set;
for (int j = 0; j < s; j++) {
cin >> skills[j];
skill_set.insert(skills[j]);
}
bool ok = true;
if (exp < min_exp) ok = false;
if (score < min_score) ok = false;
if (salary > max_salary) ok = false;
if (ok) {
for (const string& need : required) {
if (skill_set.find(need) == skill_set.end()) {
ok = false;
break;
}
}
}
if (ok) {
int matched_optional = 0;
for (const string& skill : skills) {
if (required.find(skill) == required.end()) {
matched_optional++;
}
}
long long rating = 1LL * score * 10 + 1LL * matched_optional * 5 + 1LL * exp * 2 - salary / 10000;
passed.push_back({-rating, name});
}
}
sort(passed.begin(), passed.end());
if (passed.empty()) {
cout << "NO CANDIDATES\n";
} else {
for (const auto& p : passed) {
cout << p.second << "\n";
}
}
return 0;
}
7. Код на Python 3
min_exp, min_score, max_salary, r = map(int, input().split())
required_line = input().split()
required = set(required_line)
n = int(input())
passed = []
for _ in range(n):
name, exp, score, salary, s = input().split()
exp = int(exp)
score = int(score)
salary = int(salary)
s = int(s)
skills = input().split()
skill_set = set(skills)
ok = True
if exp < min_exp:
ok = False
if score < min_score:
ok = False
if salary > max_salary:
ok = False
if ok:
for need in required:
if need not in skill_set:
ok = False
break
if ok:
matched_optional = 0
for skill in skills:
if skill not in required:
matched_optional += 1
rating = score * 10 + matched_optional * 5 + exp * 2 - salary // 10000
passed.append((-rating, name))
passed.sort()
if not passed:
print("NO CANDIDATES")
else:
for _, name in passed:
print(name)
Комментарии