1

I’d like to ask for a little bit of help with a project I’m currently working on, where I’m supposed to create a sort of “organizing program” using backtracking.

The story is, I have a given number of animals, each with their own type, habitat, and size as attributes. I’m also given a list of integers, which includes all the possible cage sizes these animals can be placed into. Of each cage size, I can create an infinite number of actual cages. Two (or more) animals can be kept in the same cage for as long as the sum of their size is less than or equal to that of the size of the cage, and they are either the same type (mammals for example) or share the same habitat (e.g. land).

My algorithm is supposed to sort them into cages so that at the end, I get a list of cages with all animals placed and using the fewest possible cages.

To illustrate, let’s say we have 3 animals, two cats (mammal, land, 3) and a vulture (bird, air, 4), and our possible cage sizes have been given as 3, 5, 7. Obviously, just by looking at this example, we can see that while the cats can be in the same cage, the vulture will inevitably land in its own, so the fewest possible cages to use is obviously going to be 2. This can either be {7: cat, cat; 5: vulture} or {7: cat, cat; 7: vulture}.

The way I had initially though of setting up my backtrack algorithm through recursion was the following:

  • N: total number of animals (in the above example, this would be 3
  • M: a list of numbers that can be used to create our cages (again, this would be three)
  • R: I was thinking of creating a list empty cages here, using the sizes that were given, but for now I just straight up used the integers
  • E: the current cycle’s solution
  • OPT: the best possible scenario so far, whenever a cycle’s E contains fewer cages than the OPT, that gets saved as the new OPT.

two levels of check

  • f1: to check if the given animal can fit in the given cage, if it is empty
  • f2: to check if the given animal could fit in any of the previous cages, given that there is still enough room and that it can be placed in the same cage with all other animals.

This way, the algorithm would first take an animal, and go through all available cage sizes, checking first if they can fit by themselves, and if yes, it’ll also check whether it could be placed in a previous cage. If it can’t, it’ll create a new cage and add the animal, then add the cage to E, but if it can be placed into a previous cage, it will do as much.

Obviously, this isn’t working just quite right. I asked my TA for some help, and he suggested I instead consider the following:

  • N: number of animals
  • M: N minus level, meaning that as I move more and more inward, I would have eliminated the need to place a certain number of animals. So for example, on the first level, I still need to place 3 animals, while on the second level only 2, and on the final level only 1.
  • R: Animals in cages (? I didn’t quite understand what he meant here, and his deeper explanations were of no help either)
  • E: list of cages, same as above
  • f1: to check whether the animal could be placed in the given cage, and whether it can be kept together with all other animals already in it
  • f2: to check whether it could have been placed in any of the other cages we put together

Now, as I said, I currently can’t fully grasp his solution, and although mine does work to a certain extent it does have its flaws (for example, it only checks for the first possible cage an animal could be placed into, without checking all other possibilities). I’m gonna copy my code here, which currently looks like this, but honestly I’m more looking for some help in finding (and/or comprehending) a better solution.

Sorry this got so long-winded, I might be overthinking/overexplaining this but I'm feeling pretty desperate at this stage haha. Any help would be GREATLY appreciated!

public virtual void Trial(int level, ref CageList<Cage> E, ref CageList<Cage> OPT, ref bool found)
        {
            int i = -1;

            while (i < M[level]-1)
            {
                i++;
                ListObj<int> R_obj = R.FindIndex(i);


                if (f1(szint, R_obj))
                {
                    int k = 0; 

                    if (E.head == null)
                    {
                        AddFirsttoE(level, R_obj, ref E);
                    }
                    else
                    {
                        while (k < E.Count() && !f2(level, E.FindIndex(k), k, ref E, R_obj))            
                        {
                            k++;
                        }
                    }


                    if (level == N - 1)
                    {
                        if(!found || E.Count() < OPT.Count())
                        {
                            OPT = E;
                        }
                        found = true;
                    }
                    else
                    {
                        Trial(level + 1, ref E, ref OPT, ref found);
                    }
                }
                freeE(level, ref E);
            }
        } 

while my two other functions are:

public override bool f1(int level, ListObj<int> obj)
        {

            bool ret = false;
            ListaObj<Animal> test = Animals.FindIndex(level);

            if (((int)test.Content.Size).CompareTo(obj.Content) <= 0)
            {
                ret = true;
            }
            return ret;
        }
public override bool f2(int level, ListObj<Cage> obj, int level2, ref CageList<Cage> E, ListObj<int> empty_size)
        {
            bool ret = false;
            ListObj<Animal> test = Animals.FindIndex(level);
            bool can_keep_together = true;

            if (((int)test.Content.Size).CompareTo(obj.Content.CageSize) <= 0)  
            {
                AnimalList<Animal> caged = obj.Content.Animals;
                ListObj<Animal> p = caged.head;

                while (p != null && can_keep_together)                                   
                {
                    if (!p.Content.CanKeepTogether(test.Content))
                    {
                        can_keep_together = false;
                    }
                    p = p.Next;
                }

                if (can_keep_together)  
                {
                    ret = true;
                    obj.Content.Animals.Add(test.Content);
                    obj.Content.CageSize -= (int)test.Content.Size;
                }
            }
            else if (level2 == E.Count() - 1)  
            {
                ret = true;
                AnimalList<Animal> new_animal_list = new AnimalList<Animal>();
                new_animal_list.Add(test.Content);
                Cage new_cage = new Cage(empty_size.Content, new_animal_list);
                new_cage.CageSize -= (int)test.Content.Méret;
                E.Add(new_cage);
            }

            if (!can_keep_together && level2 == E.Count()-1)
            {
                ret = true;
                AnimalList<Animal> new_animal_list = new AnimalList<Animal>();
                new_animal_list.Add(test.Content);
                Cage new_cage = new Cage(empty_size.Content, new_animal_list);
                new_cage.CageSize -= (int)test.Content.Méret;
                E.Add(new_cage);
            }
            return ret;
        }
Matt
  • 21
  • 3
  • I usually recommend using paper and pencil to understand algorithm. You could use a deck of cards to help you visualize the problem. – jdweng Apr 26 '22 at 17:01
  • like, I get the basics of how backtracking works vis a vis stepping out of recursion and then trying again with a different possible solution while factoring in all the decisions made up until that point; my main struggle comes from applying it to this specific issue, where I have to consider both the cage sizes, as well as all the partial solutions somehow at the same time. So I'm guessing I'd need an additional cycle somewhere in there, that lets me got through the previous solutions one by one instead of the one i'm using right now... still can't make sens of my teacher's solution though. – Matt Apr 26 '22 at 20:21
  • I don't understand a thing "I can create an infinite number of actual cages". Take always the bigger one and fill in it as many animals as you can. I think there should be some other "occupy as little space as possible with cages" type restriction. Is this the case? – Victor Apr 26 '22 at 20:40
  • nope, no restriction whatsoever, just that the solution should use as few cages as possible. you're totally right in that I should always just select the largest cage, and use that across the board, but I'm guessing that would make it too easy, so that's why I'm asked to use all possible sizes. When I said I can create an infinite number of each, I meant like I can have N cages of size 7, or I can have N-3 7s and 3 5s, or any other variety... No idea why this is the way it is to be honest. – Matt Apr 26 '22 at 20:44
  • This type of problem is called "A Packing Problem". The Romans tried to solve the problem 2000 years ago. When going to war the Romans need to pack their chariots. If they put to much in one chariot it would go slow and tip over. If they put too little in a chariot they would need more chariots. So you need a criteria for determining best solution and then try every combination. They may be smart ways of getting a fast solution that may not be the best. Scientist are still experimenting with different algorithms and do not have a single algorithm that always works. – jdweng Apr 27 '22 at 08:48

0 Answers0