Перебор всех вариантов
помогите пожалуйста решить задачу:
Мне нужно создать алгоритм, который будет находить подходящее расписания для школы ( у нас есть определенное количество классов с разными уроками, которые нужно провести за 6 дней обучения, для каждого урока есть свой учитель, свои ограничения (например, большинство уроков нужно размещать не больше раза в день, некоторые уроки нужно проводить в субботу и т.д.).
Я не прошу решить за меня, просто может подскажете, какой принцип можно для этого использовать, что почитать и т.д.
З.Ы. Был вот такой вот алгоритм, но чем дальше пишу программу, тем больше минусов в нём нахожу
private void generate_Click(object sender, RoutedEventArgs e)
{
bool tooMuchLessons = false; string tooMuchLessonsFormName = "";
bool tooMuchLessonsToTeach = false; string tooMuchLessonsToTeachName = ""; // переменные, содержащие информацию о превышении лимита уроков для учителя/класса в неделю
r.Next();
List<L> startLesson = new List<L>(); // список, в котором хранятся данные о первом уроке для перебора
List<int> startLessonF = new List<int>(); // список, в котором хранятся данные о первок уроке для перебора ( он не изменяется в будущем )
int[] backs = new int[forms.Count];
foreach(Form form in forms)
{
int k = 0;
foreach (Lesson lesson in form.lessons) k += lesson.amount;
if(k > Form.MaxLessonsAmount) { tooMuchLessons = true; tooMuchLessonsFormName = form.number.ToString() + " \"" + form.letter.ToString() + "\""; }
// проверили превышение лимита уроков для класса в неделю
L temp = new L(r.Next(0, form.lessons.Count - 1), form.lessons.Count - 1);
startLesson.Add(temp);
startLessonF.Add(temp.GetValue());
// для каждого класса задан случайный первый урок и это значение сохранено
}
foreach (Teacher teacher in teachers)
{
int k = 0;
foreach (Form form in forms) foreach (Lesson lesson in form.lessons) if (lesson.teach.fullname == teacher.fullname) k += lesson.amount;
if (k > Teacher.MaxLessonsAmount) { tooMuchLessonsToTeach = true; tooMuchLessonsToTeachName = teacher.fullname; }
} // проверяет превышение лимита уроков для учителя в неделю
if(!tooMuchLessons && !tooMuchLessonsToTeach) // если не превышен лимит уроков для класса/учителя, то начинается перебор расписаний
{
schedules = new List<Schedule>(); // Расписания полностью обнуляются
tschedules = new List<tSchedule>(); // Учительские расписания полностью обнуляются
foreach (Form form in forms) schedules.Add(new Schedule()); // Создаются расписания, соотвествующие каждому классу
foreach (Teacher teacher in teachers) tschedules.Add(new tSchedule()); // Создаются расписания, соответствующие каждому учителю
for (int f_id = 0; f_id < tschedules.Count; f_id++)
{
for (int d_id = 0; d_id < 6; d_id++)
{
for (int l_id = 0; l_id < 12; l_id++)
{
tschedules[f_id].days[d_id].lessons[l_id] = new tLesson("", "");
}
}
}
for (int form = 0; form < forms.Count; form++) // Перебираем каждый отдельный класс
{
Lesson[] LSN = new Lesson[forms[form].lessons.Count];
forms[form].lessons.CopyTo(LSN);
Lesson T;
for (int i = 0; i < LSN.Count(); i++)
for (int k = 0; k < LSN.Count(); k++)
if (LSN[i].sub.toughness > LSN[k].sub.toughness)
{
T = LSN[i];
LSN[i] = LSN[k];
LSN[k] = T;
}
List<int> LessonsLeft; // Переменная, отвечающаяя за количество оставшихся на неделю определенных уроков
int LessonsAtAll = -1; // Переменная, отвечающая за количество оставшихся на неделю ВСЕХ уроков
L LESSON; // Мы создаем переменную для перебора возможных уроков
L startLessonT = new L(startLesson[form].GetValue(), LSN.Count() - 1); // Создаем переменную, которая будет меняться в случае неудачных расписаний
int shift = 0; // По стандарту задаем смещение на 0
int s = 0; int f = 0;
if (forms[form].secondShift) shift = 6; // Если у класса вторая смена, то смещение на 6
if (shift == 0)
{
s = 0; f = 6;
}
else
{
s = 5; f = 11;
}
for (int day = 0; day < 6; day++) // Перебираем каждый день для чистки учительского расписания (удаляем только те уроки, которые ведут у этого класса)
for (int lesson = 0; lesson < 12; lesson++) // Перебираем уроки
for (int tsched = 0; tsched < tschedules.Count; tsched++) // Проверяем расписание каждого учителя
if (tschedules[tsched].days[day].lessons[lesson].form != "" && tschedules[tsched].days[day].lessons[lesson].form == (forms[form].number.ToString() + forms[form].letter)) // Условия обнуления
tschedules[tsched].days[day].lessons[lesson] = new tLesson("", ""); // Само обнуление
bool formFailed = false; // Переменная = true, если расписание сгенерировать не удалось
int backSave = -1;
for (int weekImplementations = 0; weekImplementations < LSN.Count() && LessonsAtAll != 0; weekImplementations++) // Если сгенерировать расписание не удалось, то повторяем, пока не закончатся варианты
{
LessonsLeft = new List<int>(); // Обнуляем для правильного подсчета
LessonsAtAll = 0; // Обнуляем для правильного подсчета
LESSON = new L(startLessonT.GetValue(), LSN.Count() - 1); // Мы создаем переменную для перебора возможных уроков
foreach (Lesson lesson in LSN)
{
LessonsLeft.Add(lesson.amount); // Вводим значения количества определенных уроков на неделю
LessonsAtAll += lesson.amount; // Вводим значения количество ВСЕХ уроков на неделю
}
//MessageBox.Show(LessonsAtAll.ToString());
foreach(int day in days) // Перебираем каждый отдельный день
{
List<int> LessonsADayLeft = new List<int>(); // Переменная, отвечающая за количество оставшихся на день определенных уроков
foreach (Lesson lesson in LSN) LessonsADayLeft.Add(lesson.aday); // Вводим значения количества определенных уроков на день
for (int lesson = 0; lesson < 12; lesson++)
schedules[form].days[day].lessons[lesson] = new Lesson(0, 0, new Subject("", 0), new Teacher(" ", " ", " ", null), false, false); // Предварительно заполняем классное расписание пустыми уроками
for (int lesson = s; lesson <= f; lesson++) // Перебираем каждый отдельный урок (первый равен смещению, последний равен 6 + смещение (чтобы уроки размещались на нужной смене)
{
bool DoubleCheck = false;
foreach (Lesson l1 in schedules[form].days[day].lessons)
foreach (Lesson l2 in schedules[form].days[day].lessons)
if (l1 == l2) DoubleCheck = true;
bool passedSaturdayCheck = (LSN[LESSON.GetValue()].saturday == (day == 5)); // Субботняя проверка
bool passedAdditionalCheck = (LSN[LESSON.GetValue()].additional == ((lesson == 6 && shift == 0) || (lesson == 5 && shift == 6)));
//MessageBox.Show(passedAdditionalCheck.ToString());
bool teacherUnavailable = (tschedules[teachers.IndexOf(LSN[LESSON.GetValue()].teach)].days[day].lessons[lesson].form != ""); // Проверяем, свободен ли учитель
int implementations = LSN.Count(); // Количество попыток на установление урока
bool passedDoubleCheck = (DoubleCheck && LSN[LESSON.GetValue()].aday == 2 && LessonsADayLeft[LESSON.GetValue()] == 1);
while ((passedDoubleCheck || !passedSaturdayCheck || !passedAdditionalCheck || teacherUnavailable || LessonsLeft[LESSON.GetValue()] == 0 || LessonsADayLeft[LESSON.GetValue()] == 0) && implementations > 0)
{
LESSON.inc(); // Если урок не подходит по какому-то из параметров, то мы меняем урок, который хотим поставить
passedSaturdayCheck = (LSN[LESSON.GetValue()].saturday == (day == 5)); // Субботняя проверка, после обновления
passedAdditionalCheck = (LSN[LESSON.GetValue()].additional == ((lesson == 6 && shift == 0) || (lesson == 5 && shift == 6)));
passedDoubleCheck = (DoubleCheck && LSN[LESSON.GetValue()].aday == 2 && LessonsADayLeft[LESSON.GetValue()] == 0);
teacherUnavailable = (tschedules[teachers.IndexOf(LSN[LESSON.GetValue()].teach)].days[day].lessons[lesson].form != ""); // Проверяем, свободен ли учитель после обновления
implementations--; // Уменьшаем количество оставшихся попыток
}
if (implementations == 0) schedules[form].days[day].lessons[lesson] = new Lesson(0, 0, new Subject(" ", 0), new Teacher(" ", " ", " ", null), false, false); // Если попытки закончились, то устаналвиваем пропускной урок
else // А если мы нашли подходящий урок, то
{
schedules[form].days[day].lessons[lesson] = LSN[LESSON.GetValue()]; // Устанавливаем найденный урок
LessonsADayLeft[LESSON.GetValue()]--; LessonsLeft[LESSON.GetValue()]--; // Уменьшаем количество оставшихся уроков в день и в неделю
LessonsAtAll--; // Уменьшаем количество уроков, которые все ещё нужно куда-то установить
}
} // Конец цикла перебора уроков
} // Конец цикла перебора
//MessageBox.Show(LessonsAtAll.ToString());
startLessonT.inc(); // Меняем первый урок на случай, если расписание не сгенерировалось и мы снова идем по кругу
}
startLesson[form].inc(); // Меняем стартовый урок для класса на случай возвращения сюда
if (LessonsAtAll != 0) { formFailed = true; } // Если не все уроки установлены в расписании, то нам не удалось его сгенерировать
if ((backs[0] == LSN.Count() || forms.Count == 1) && formFailed == true) { FAILED = true; break; } // Если первый класс перегенерировался уже максимальное количество раз, то прекращаем пытаться'
int tempHowBack = 0; // Создаем переменную для возвращения
if(form > 0) for (tempHowBack = 1; (form - tempHowBack >= 0 && backs[form - tempHowBack] == LSN.Count()); tempHowBack++) ; // Определяем как сильно назад нам нужно вернуться
if (backSave == form && !formFailed) for (int i = 0; i < backs.Count(); i++) backs[i] = 0; // Если нам удалось сгенерировать то очищаем количество возвращений
if (form > 0 && formFailed) { backSave = form; form -= (1 + tempHowBack); backs[form+1]++;} // Если не удалось, то возвращаемся на класс назад и создаем ему другое расписание
if (LessonsAtAll == 0) { formFailed = false; } // Если сгенерировалось, то не провалено лол
if (!formFailed) // Если провалено
{
for (int day = 0; day < 6; day++) // Перебираем дни для записи в учительское расписание
{
for (int lesson = 0; lesson < 12; lesson++) // Перебираем уроки для записи в учительское расписание
{
if (schedules[form].days[day].lessons[lesson].teach.fullname != " ") // Если урок не пустой, то
tschedules[teachers.IndexOf(schedules[form].days[day].lessons[lesson].teach)].days[day].lessons[lesson] = new tLesson(forms[form].fullname, schedules[form].days[day].lessons[lesson].sub.name);
// Записали урок в учительское расписание
} // Конец перебора уроков для учителей
} // Конец перебора дней для учителей
}
formFailed = false;
} // Конец цикла перебора классов
}
if (FAILED) this.ShowMessageAsync("Ошибка!", "Не удалось сгенерировать расписание с заданными условиями, попробуйте изменить их.");
object save = FormsList.SelectedItem;
FormsList.SelectedItem = null;
FormsList.SelectedItem = save;
}<code>