Pergunta

I am trying to figure out how to solve this problem..it is taken from a programming competition held for grade 12 students. The task is to have the student 'Karli' take enough classes to obtain 214 credits. The student can't take more or less than 214 credits before entering the exam room. The doors are represented in the diagram. The user may repeat a class for additional classes, but they must leave that classroom..go to another classroom..and then come back.

I tried to do this manually and was able to find one solution with path:

Math-algebra-philosophy-algebra-math-modeling-calculus-modeling-exam

I am trying to develop an algorithm that will find a path given the number of credits required (i.e 214 for this case)

here is what I've tried and gotten stuck on :

Representing the map as a graph with the door being a double edge between the two nodes. However I don't know what graph traversal algorithm will allow me to solve this problem?

Would converting the graph to an adjacency matrix make things any easier?

Thanks

Foi útil?

Solução

Breadth First Search will solve this problem.

Record the status (room, credit) when Karli arrived at room numbered as room with a credit value recorded in credit.

Using a queue to maintain the data. At the beginning, only (outside, 0) is in the queue. Each time, pop the head, and move from the status described by head to each neighbour room of head, then calculate the new status and push them into the end of the queue(remember to use a hash to avoid a same status being added more than once).

When you reach a status (exam, 214), the expand process finishes. The remaining work is to traverse back from the status (exam, 214). When getting a new status in BFS, you can also record the pointer to precursor status.

Here is my code.

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: Sorry I make some mistakes when assigning values to neighbour[]. I hava corrected it.

UPD1: Sorry I forget to check whether credit is 214 when enter the exam room. I hava corrected it.

UPD3: @Nuclearman says that it does not give all the solutions. We only need to remove hash from the code, and calculate the path when generating a new status with credit 214. I give the new code here.

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;
        }
}

Outras dicas

Here's a version in Haskell, generating paths backwards from the exam room and discarding paths with credit sums greater than the requirement:

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))

Output:

*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)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top