Алгоритм поиска пути (расписание занятий)

Пытаюсь понять, как решить эту проблему ... это взято из соревнований по программированию, проводимых для учеников 12 класса. Задача состоит в том, чтобы ученица Карли набрала достаточно уроков, чтобы получить 214 кредитов. Студент не может набрать больше или меньше 214 кредитов перед тем, как войти в экзаменационную комнату. Двери представлены на схеме. Пользователь может повторить урок для дополнительных классов, но он должен покинуть этот класс .. перейти в другой класс .. и затем вернуться.

Я попытался сделать это вручную и смог найти одно решение с путем:

Математика-алгебра-философия-алгебра-математическое моделирование-исчисление-моделирование-экзамен

Я пытаюсь разработать алгоритм, который найдет путь с учетом необходимого количества кредитов (например, 214 для этого случая)

вот что я пробовал и на чем застрял:

Представление карты в виде графа, в котором дверь представляет собой двойное ребро между двумя узлами. Однако я не знаю, какой алгоритм обхода графа позволит мне решить эту проблему?

Может ли преобразование графа в матрицу смежности упростить задачу?

Спасибо


person user1411893    schedule 26.05.2013    source источник
comment
Может ли Карли выйти из математической комнаты через нижнюю дверь и войти в экзаменационную через левую?   -  person konjac    schedule 26.05.2013
comment
Нет. Нижняя математическая комната предназначена только для входа, а левая экзаменационная - только для выхода. Единственный способ войти в смотровую - через комнату моделирования.   -  person user1411893    schedule 26.05.2013
comment
Обязательно ли Керли вести занятия в той комнате, в которую он входит?   -  person konjac    schedule 26.05.2013
comment
да. Каждый раз, когда он входит в комнату, предполагается, что уроки прошли, и он зарабатывает кредиты. Так что независимо от того, что он начнет с математического класса и заработает 17 кредитов, а закончит с классом моделирования и заработает 29 кредитов.   -  person user1411893    schedule 26.05.2013
comment
@ user1411893 Хотел добавить лучшее описание к двум приведенным ниже решениям. Оба они выглядят как алгоритмы обратного отслеживания: en.wikipedia.org/wiki/Backtracking. Идея по сути является грубой силой. Начните с математической комнаты, попробуйте другую комнату. Продолжайте пытаться, пока не потерпите неудачу, затем отмените класс и попробуйте что-нибудь еще. Интуитивно грубая сила даст вам все ответы (если таковые имеются), но обычно она не очень эффективна, если график становится большим.   -  person rliu    schedule 26.05.2013


Ответы (2)


Breadth First Search решит эту проблему.

Запишите статус (room, credit), когда Карли прибыла в комнату с номером room со значением кредита, записанным в credit.

Использование queue для хранения данных. Вначале в очереди находится только (снаружи, 0). Каждый раз выталкивайте голову и переходите из состояния, описанного head, в каждую соседнюю комнату head, затем вычисляйте новый статус и вставляйте их в конец queue (не забудьте использовать хэш, чтобы избежать добавления одного и того же статуса). чем один раз).

Когда вы достигнете статуса (exam, 214), процесс развертывания завершится. Остается вернуться назад из состояния (exam, 214). При получении нового статуса в BFS вы также можете записать указатель на статус-предшественник.

Вот мой код.

char name[][15] = {
        "exam",
        "stochastic",
        "modeling",
        "calculus",
        "math",
        "modern arts",
        "algebra",
        "philosophy",
        "outside"
};

int credits[]={0, 23, 29, 20, 17, 17, 35, 32, 0};

int neighbour[][7]={
        { 1, 2, -1},
        { 2, 3, -1},
        { 0, 1, 3, 4, 5, -1},
        { 1, 2, 4,-1},
        { 2, 3, 6, -1},
        { 2, 6, 7, -1},
        { 4, 5, 7, -1},
        { 5, 6, -1},
        { 4, -1}
};


class Node{
public:
        int pos;
        int credit;
        bool operator <( const Node a) const{
                return pos < a.pos || pos == a.pos && credit < a.credit;
        }
};

vector<Node> Q;
vector<int> pred;
set<Node> hash;
void bfs(){
        int n = 9;
        bool found = false;
        hash.clear();
    Node start;
        start.pos = 8, start.credit = 0;
        Q.push_back(start);
        pred.push_back(-1);
        hash.insert(start);
        for(int f=0; f<Q.size(); ++f){
                Node head = Q[f];
                int pos = head.pos;
                //printf("%d %d -> \n", head.pos, head.credit);
                for(int i=0; neighbour[pos][i]!=-1; ++i){
                        Node tmp;
                        tmp.pos = neighbour[pos][i];
                        tmp.credit = head.credit + credits[tmp.pos];
                        if(tmp.credit > 214) continue;
                        if(hash.count(tmp)) continue;
                        if(tmp.credit !=214 && tmp.pos==0)continue;   // if the credit is not 214, then it is not allowed to enter exame room(numbered as 0)
                        Q.push_back(tmp);
                        pred.push_back(f);
                        //printf("    -> %d, %d\n", tmp.pos, tmp.credit);
                        if(tmp.credit==214 && tmp.pos==0){
                                found = true;
                                break;
                        }
                }
                if(found)break;
        }
        stack<int> ss;
        int idx = Q.size()-1;
        while(true){
                ss.push(Q[idx].pos);
                if(pred[idx]!=-1) idx=pred[idx];
                else break;
        }
        for(int credit=0; ss.size() > 0; ){
                int pos = ss.top();
                credit += credits[pos];
                printf("%s(%d) ", name[pos], credit);
                ss.pop();
        }
        printf("\n");
}

UPD1: Извините, я допускаю ошибки при присвоении значений neighbour[]. Я исправил это.

UPD1: Извините, я забыл проверить, равен ли балл 214 при входе в экзаменационную комнату. Я исправил это.

UPD3: @Nuclearman говорит, что не дает всех решений. Нам нужно только удалить hash из кода и вычислить путь при создании нового статуса с кредитом 214. Здесь я привожу новый код.

char name[][15] = {
        "exam",
        "stochastic",
        "modeling",
        "calculus",
        "math",
        "modern arts",
        "algebra",
        "philosophy",
        "outside"
};

int credits[]={0, 23, 29, 20, 17, 17, 35, 32, 0};

int neighbour[][7]={
        { 1, 2, -1},
        { 2, 3, -1},
        { 0, 1, 3, 4, 5, -1},
        { 1, 2, 4,-1},
        { 2, 3, 6, -1},
        { 2, 6, 7, -1},
        { 4, 5, 7, -1},
        { 5, 6, -1},
        { 4, -1}
};


class Node{
public:
        int pos;
        int credit;
        bool operator <( const Node a) const{
                return pos < a.pos || pos == a.pos && credit < a.credit;
        }
};

vector<Node> Q;
vector<int> pred;
set<Node> hash;

void outputpath(){
        stack<int> ss;
        int idx = Q.size()-1;
        while(true){
                ss.push(Q[idx].pos);
                if(pred[idx]!=-1) idx=pred[idx];
                else break;
        }
        for(int credit=0; ss.size() > 0; ){
                int pos = ss.top();
                credit += credits[pos];
                printf("%s(%d) ", name[pos], credit);
                ss.pop();
        }
        printf("\n");
}

void bfs(){
        int n = 9;
        bool found = false;
        hash.clear();
        Node start;
        start.pos = 8, start.credit = 0;
        Q.push_back(start);
        pred.push_back(-1);
        hash.insert(start);
        for(int f=0; f<Q.size(); ++f){
                Node head = Q[f];
                int pos = head.pos;
                for(int i=0; neighbour[pos][i]!=-1; ++i){
                        Node tmp;
                        tmp.pos = neighbour[pos][i];
                        tmp.credit = head.credit + credits[tmp.pos];
                        if(tmp.credit > 214) continue;
                        if(hash.count(tmp)) continue;
                        if(tmp.credit !=214 && tmp.pos==0)continue;
                        Q.push_back(tmp);
                        pred.push_back(f);
                        if(tmp.credit==214 && tmp.pos==0){
                                outputpath();
                                /* uncomment the next line to get only one solution*/
                                //found = true;
                                break;
                        }
                }
                if(found)break;
        }
}
person konjac    schedule 26.05.2013
comment
Я думаю, что это не предоставит всех решений, поскольку класс можно использовать несколько раз, что недопустимо в BFS. - person Nuclearman; 26.05.2013
comment
@Nuclearman Автору @ user1411893 кажется, что нужно только одно решение. Но если вам действительно нужны все решения, вы можете отказаться от hash и только проверить, не превышает ли кредит 214, чтобы все возможные решения можно было посещать по мере роста очереди. Более того, "a class may be taken multiple times, which isn't allowed in BFS" работает неправильно, что зависит от того, как вы управляете операциями постановки в очередь. - person konjac; 26.05.2013
comment
@Nuclearman Действительно, мое решение уже позволяет использовать класс несколько раз. _1 _- ›_ 2 _-› _ 3 _- ›_ 4 _-› _ 5 _- ›_ 6 _-› _ 7 _- ›_ 8 _-› _ 9 _- ›_ 10_. - person konjac; 26.05.2013
comment
Если это уже было в вашем коде, приношу свои извинения за то, что это было возможно. - person Nuclearman; 27.05.2013
comment
Кун Хуанг спасибо! Только один вопрос, что это должно делать? : bool operator ‹(const Node a) const {return pos‹ a.pos || pos == a.pos && credit ‹a.credit; } Я пытаюсь написать это на java - person user1411893; 28.05.2013
comment
@ user1411893 Он определяет порядок между двумя объектами Node или comparator функцией класса Node, что необходимо для использования set. В Java вместо этого можно использовать HashMap или HashSet. Им также нужен класс Comparator. - person konjac; 28.05.2013
comment
как насчет пред? для чего ты это используешь - person user1411893; 28.05.2013
comment
@ user1411893 pred[i] записывает индекс предшественника Node в Q. Другими словами, «Q [i]« генерируется из Q[pred[i]]. pred[i] используется для поиска пути. - person konjac; 29.05.2013
comment
Разве Q не очередь? зачем вам нужно следить за предшественником? Разве удаление каждого элемента из очереди не приведет к обратному пути? (который вы добавляете в стек для представления пути) - person user1411893; 29.05.2013
comment
@ user1411893 В очереди хранится много дополнительных узлов. Следить за предшественниками будет эффективнее. Я использую стек по той причине, что мой код отслеживает обратный путь от exam комнаты до начала, но нам нужно вывести его от начала до exam комнаты. stack можно не указывать, если BFS от exam комнаты до начала. - person konjac; 29.05.2013

Вот версия на Haskell, генерирующая пути назад от экзаменационной комнаты и отбрасывающая пути с суммой кредитов, превышающей требуемую:

import Data.Maybe (fromJust)
import Control.Monad (guard)

classes = [("exam",["modeling"])
          ,("modeling",["exam","stochastic","calculus","math","modern arts"])
          ,("stochastic",["calculus","modeling"])
          ,("calculus",["stochastic","modeling","math"])
          ,("math",["calculus","modeling","algebra"])
          ,("algebra",["math","philosophy"])
          ,("philosophy",["algebra","modern arts"])
          ,("modern arts",["philosophy","modeling"])]

credits = [("exam",0)
          ,("modeling",29)
          ,("stochastic",23)
          ,("calculus",20)
          ,("math",17)
          ,("algebra",35)
          ,("philosophy",32)
          ,("modern arts",17)]

solve requirement = solve' ["exam"] 0 where
  solve' path creditsSoFar =
    if creditsSoFar == requirement && head path == "math"
       then [path]
       else do
         next <- fromJust (lookup (head path) classes)
         guard (next /= "exam" 
                && creditsSoFar + fromJust (lookup next credits) <= requirement)
         solve' (next:path) (creditsSoFar + fromJust (lookup next credits))

Вывод:

*Main> solve 214
[["math","algebra","philosophy","algebra","math","modeling","calculus","modeling","exam"]
,["math","calculus","math","calculus","math","calculus","math","calculus","math","calculus","modeling","exam"]
,["math","algebra","philosophy","modern arts","philosophy","algebra","math","modeling","exam"]]
(0.19 secs, 9106396 bytes)
person גלעד ברקן    schedule 26.05.2013