Click here to Skip to main content
15,881,172 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I have n sets of two items:
["a", "b"]
["c", "d"]
["d", "a"]
["b", "c"]

And I want to find at least one item from this set (if it exists), that passes the following constraints:

The first constraints is a sort of exclusion check (= we are given a list of sets of items that cannot exist together), this set may look something like this:
["c", "d"] // items "c" and "d" cannot exist together
...

The second constraint specifies which items need which items to exist (= we are given list of sets of items that have to exist together), this set may look something like this:
["b", "c"] // item "b" needs item "c" and vice versa
...


To solve for these constraints we can use items from other sets from the original set (= the first set in this question).

The solution for the example sets is as follows (note that there may exist more that one solution, we just need a valid one):
Passed checks with items: 
a
b
c


Some of the caveats I've found out while working on this problem:
- The solution has to be recursive
- We need to consider that an item may be "locked" because of an exclusion created by other items, which may be worked around if we were to use a different combination of items.

Any ideas about how to get this working are very much appreciated.

What I have tried:

#include <iostream>
#include <vector>
#include <unordered_set>

using namespace std;

class Set {
public:
    std::string item1;
    std::string item2;

    Set(std::string i1, std::string i2) : item1(i1), item2(i2) {}
};

// Define a function to check if a given pair of items violates a constraint
bool ViolatesConstraints(std::string item1, std::string item2, const vector<pair<std::string, std::string>>& constraints) {
    for (const auto& constraint : constraints) {
        if ((constraint.first == item1 && constraint.second == item2) ||
            (constraint.first == item2 && constraint.second == item1)) {
            return true;
        }
    }
    return false;
}

// Define a function to recursively find all required items
void FindRequiredItems(std::string item, const vector<pair<std::string, std::string>>& inclusions, unordered_set<std::string>& requiredItems) {
    for (const auto& inclusion : inclusions) {
        if (inclusion.first == item) {
            requiredItems.insert(inclusion.second);
            FindRequiredItems(inclusion.second, inclusions, requiredItems);
        }
    }
}

int main() {
    // Define the list of sets and their items
    vector<Set> sets = { ... };

    // Define the list of constraint pairs
    vector<pair<std::string, std::string>> constraints = { ... };

    // Define the list of inclusion pairs
    vector<pair<std::string, std::string>> inclusions = { ... };

    // Initialize the best solution found so far
    bool passedChecks = false;
    unordered_set<std::string> usedItems;

    // Iterate over all sets to search for a solution
    for (const auto& set : sets) {
        // Check if the set's items violate any constraints
        if (ViolatesConstraints(set.item1, set.item2, constraints)) {
            continue;
        }

        // Check if the set's items satisfy all inclusions recursively
        unordered_set<std::string> requiredItems = { set.item1, set.item2 };
        for (const auto& requiredItem : requiredItems) {
            FindRequiredItems(requiredItem, inclusions, requiredItems);
        }
        if (requiredItems.size() == 5) {
            // Found a solution, update the best solution found so far
            passedChecks = true;
            usedItems.clear();
            usedItems.insert(set.item1);
            usedItems.insert(set.item2);
        }
        else {
            // Current solution doesn't satisfy all inclusions, continue searching
            continue;
        }

        // Search for a better solution
        for (const auto& nextSet : sets) {
            if (nextSet.item1 == set.item1 && nextSet.item2 == set.item2) {
                continue;  // skip current set
            }

            if (ViolatesConstraints(nextSet.item1, nextSet.item2, constraints)) {
                continue; // skip set whose items violate constraints
            }

            unordered_set<std::string> requiredItems = { nextSet.item1, nextSet.item2 };
            for (const auto& requiredItem : requiredItems) {
                FindRequiredItems(requiredItem, inclusions, requiredItems);
            }

            if (requiredItems.size() == 5) {
                // Found a better solution, update the best solution found so far
                passedChecks = true;
                    usedItems.clear();
                    usedItems.insert(nextSet.item1);
                usedItems.insert(nextSet.item2);
            }
        }
    }

    // Print the result
    if (passedChecks) {
        cout << "Passed checks with items:";
        for (const auto& item : usedItems) {
            cout << " " << item;
        }
        cout << endl;
    }
    else {
        cout << "Failed checks" << endl;
    }

    return 0;
}
Posted
Comments
jeron1 13-Mar-23 10:04am    
Is there a specific question? Where are you having problems?
Yor Gurt 13-Mar-23 11:51am    
Hi! Yes, my proposed algorithm doesn't work, and I'd like to get advice/help on getting it working, if possible

Rick York 13-Mar-23 16:33pm    
That means it is time to learn the debugger. A side-effect of learning the debugger is it encourages you to write code that is more easily debugged. I recommend that you start at the beginning and verify that your data structures all look correct. Then check your first function - ViolatesContstraints. Make sure it works as it is supposed to and then move on to the next one.
Yor Gurt 13-Mar-23 16:47pm    
Thanks, but would you mind telling me what your opinion on the proposed algorithm is? Does it look "logically" correct or does anything here jump out to you as being specifically wrong for solving the problem at hand?
Rick York 13-Mar-23 19:22pm    
I did not see any obvious errors. One thing I don't understand is why must requiredItems.size() be 5? You have that in the code twice but there is no mention of it in the description of the problem.

One thing I prefer to do is define a type or a using statement for repeated non-trivial types. Here are some examples :
using pairstrstr  = std::pair< std::string, std::string >;
using vpairstrstr = std::vector< pairstrstr >;
using uosetstr    = std::unordered_set< std::string >;
This is because it simplifies a lot of things in my opinion. Your function prototypes would become :
bool ViolatesConstraints( std::string item1, std::string item2, const vpairstrstr & constraints );
void FindRequiredItems( std::string item, const vpairstrstr & inclusions, uosetstr & requiredItems );
I think that is much easier to read.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900