개발

[내가만든]당번 정하기

Hugh Q Lee 2022. 4. 26. 23:42

문제

  • 'A' 회사는 직원(people)이 7명이다.
  • 직원들은 월요일(monday)과 공휴일(holiday)에 당직을 서야 한다.
  • 2022년 당직을 서는 횟수를 최대한 공평하게 분배하라.
  • 월요일 횟수와 공휴일 횟수를 따로 카운트한다. (월요일 당직과 공휴일 당직은 엄연히 다르다.)
  • 월요일, 공휴일 관계없이 연속해서 당직을 서는 일이 없도록 한다. (즉, 지난 당직일에 당번이 나였는데 이번 당직일에 당번이 나면 안된다.)
import holidays
from datetime import date, timedelta

people = ['김', '나', '박', '이', '채', '신', '하']

monday_list = []
first_day = date(2022, 1, 1)
monday = first_day + timedelta(days= 7-first_day.weekday())
while monday.year == year:
	monday_list.append(monday)
    monday += timedelta(days= 7)

holiday_dict = holidays.KR(years=2022)
holiday_list = [holiday for holiday in holiday_dict.keys()]

풀이

import random

# 먼저, 공휴일인 월요일을 월요일 리스트에서 삭제합니다.
for holiday in holiday_list:
	if holiday in monday_list:
    	monday_list.remove(holiday)


# 당직일 전체 리스트를 만듭니다.
sum_list = monday_list + holiday_list
sum_list.sort()


# 직원리스트를 셔플한 후, 직원별 스코어보드를 만듭니다. {person: [cnt_monday, cnt_holiday]}
random.shuffle(people)

person_dict = {}
for person in people:
	person_dict[person] = [0, 0]
    
    
# 조건에 맞게 당직을 배정합니다.
prev = None
max_cnt_mon = 0
max_cnt_hol = 0
idx = 0
num = len(members)

for day in sum_list:
    loop = 0
    while True:
        idx %= num
        person= people[idx]
        cnt = person_dict[person]
        
        if loop < num:
            idx += 1
            if day in monday_list and cnt[0] <= max_cnt_mon:
                charge_dict[day] = person
                cnt[0] += 1
                break
            elif day in holiday_list and cnt[1] <= max_cnt_hol:
                charge_dict[day] = person
                cnt[1] += 1
                break
        else:
            if day in monday_list:
                max_cnt_mon += 1
            elif day in holiday_list:
                max_cnt_hol += 1
            loop = 0
        loop += 1

# 결과를 확인합니다.
print(person_dict)
print(charge_dict)
{'김': [7, 3],
 '나': [6, 3],
 '채': [6, 3],
 '신': [7, 2],
 '이': [7, 2],
 '박': [7, 2],
 '하': [6, 3]}
 
 {datetime.date(2022, 1, 1): '김',
 datetime.date(2022, 1, 3): '나',
 datetime.date(2022, 1, 10): '채',
 datetime.date(2022, 1, 17): '신',
 datetime.date(2022, 1, 24): '이',
 datetime.date(2022, 1, 31): '박',
 datetime.date(2022, 2, 1): '하',
 datetime.date(2022, 2, 2): '나',
 datetime.date(2022, 2, 7): '박',
 datetime.date(2022, 2, 14): '하',
 datetime.date(2022, 2, 21): '김',
 datetime.date(2022, 2, 28): '나',
 datetime.date(2022, 3, 1): '채',
 datetime.date(2022, 3, 7): '신',
 datetime.date(2022, 3, 14): '이',
 datetime.date(2022, 3, 21): '박',
 datetime.date(2022, 3, 28): '하',
 datetime.date(2022, 4, 4): '김',
 datetime.date(2022, 4, 11): '채',
 datetime.date(2022, 4, 18): '신',
 datetime.date(2022, 4, 25): '이',
 datetime.date(2022, 5, 1): '신',
 datetime.date(2022, 5, 2): '박',
 datetime.date(2022, 5, 5): '이',
 datetime.date(2022, 5, 8): '박',
 datetime.date(2022, 5, 9): '하',
 datetime.date(2022, 5, 16): '김',
 datetime.date(2022, 5, 23): '나',
 datetime.date(2022, 5, 30): '채',
 datetime.date(2022, 6, 6): '신',
 datetime.date(2022, 6, 13): '이',
 datetime.date(2022, 6, 20): '박',
 datetime.date(2022, 6, 27): '하',
 datetime.date(2022, 7, 4): '김',
 datetime.date(2022, 7, 11): '나',
 datetime.date(2022, 7, 18): '채',
 datetime.date(2022, 7, 25): '신',
 datetime.date(2022, 8, 1): '이',
 datetime.date(2022, 8, 8): '박',
 datetime.date(2022, 8, 15): '하',
 datetime.date(2022, 8, 22): '김',
 datetime.date(2022, 8, 29): '나',
 datetime.date(2022, 9, 5): '채',
 datetime.date(2022, 9, 9): '이',
 datetime.date(2022, 9, 10): '김',
 datetime.date(2022, 9, 11): '나',
 datetime.date(2022, 9, 12): '채',
 datetime.date(2022, 9, 19): '신',
 datetime.date(2022, 9, 26): '하',
 datetime.date(2022, 10, 3): '김',
 datetime.date(2022, 10, 9): '나',
 datetime.date(2022, 10, 10): '채',
 datetime.date(2022, 10, 17): '신',
 datetime.date(2022, 10, 24): '이',
 datetime.date(2022, 10, 31): '박',
 datetime.date(2022, 11, 7): '하',
 datetime.date(2022, 11, 14): '김',
 datetime.date(2022, 11, 21): '나',
 datetime.date(2022, 11, 28): '채',
 datetime.date(2022, 12, 5): '신',
 datetime.date(2022, 12, 12): '이',
 datetime.date(2022, 12, 19): '박',
 datetime.date(2022, 12, 25): '하',
 datetime.date(2022, 12, 26): '김'}

해설

for 문으로 당직일 리스트를 순회하며, while문 내에서 당직자를 배정하는 알고리즘입니다.
loop는 직원명단을 한바퀴 순회함을 체크하기 위해서 만들었습니다. 만약 한바퀴 순회할 때까지 당직자를 배정하지 못한다면 max_cnt(월요일 또는 공휴일)에 +1한 후 loop를 다시 0으로 만들어 다시 한바퀴 순회하며 배정할 수 있도록 했습니다.
idx는 직원명단에서 직원을 선택하는 인덱스로 사용하는데 나머지 연산(idx %= num)을 이용하여 0에서 마지막 번호(7명이면, 6)까지 차례로 반복하도록 했습니다.
person = people[idx]로 당직자 후보를 뽑은 후 이 후보가 (월요일, 공휴일 중 각 해당일의) max_cnt보다 낮은 score를 가지고 있다면 해당일의 당직자로 배정하고 idx에 +1하여 다음 당직일의 후보자 순회는 다음 idx의 사람부터 while문을 쓰도록 합니다. (코드의 중복을 피하기 위해서 idx += 1부터 시행했습니다. (shuffle된 직원 리스트라서 관계없다고 생각합니다.)

728x90