How to accept graph from input file - c++

I am accepting undirected weighted graph from input file. File contains 200 nodes.
I have written code like this.
typedef pair<int, int> ipair;
class Graph
{
int V;
list< pair <int, int> > *adj;
public:
Graph(int V);
};
Graph::Graph( int V)
{
this-> V = V;
adj = new list<ipair>[V];
}
bool Graph :: read_file()
{
const long int N = 1000000;
std::ifstream infile("Dijkstra.txt");
if(!infile.is_open()) return false;
std::string line;
int i = 0;
while ( i < N && getline(infile, line) )
{
std::istringstream str(line);
int u;
str >> u;
if ( u > N )
{
// Problem.
abort();
}
int v;
int w;
while ( str >> v >> w)
{
adj[u].push_back(make_pair(v,w));
}
++i;
}
}
int main()
{
Graph g(200);
g.read_file();
g.print_graph();
return 0;
}
I/P file :
1 80,982 163,8164 170,2620 145,648 200,8021 173,2069 92,647 26,4122 140,546 11,1913 160,6461 27,7905 40,9047 150,2183 61,9146 159,7420 198,1724 114,508 104,6647 30,4612 99,2367 138,7896 169,8700 49,2437 125,2909 117,2597 55,6399
2 42,1689 127,9365 5,8026 170,9342 131,7005 172,1438 34,315 30,2455 26,2328 6,8847 11,1873 17,5409 157,8643 159,1397 142,7731 182,7908 93,8177
Node 1 is connected to node 80 with weight 982
Node 1 is connected to node 163 with weight 8164
Node 2 is connected to node 42 with weight 1689
Node 2 is connected to node 127 with weight 9365
etc.....
Now with this I can accept node 1 is connected to node 80 with weight 982 (1 80,982 ) but what about remaining nodes which are connected with node 1 ??
i.e. How to make loop for accepting v and w ??

I think instead of list< pair <int, int> > *adj in Graph class you can use vector<list< pair <int, int> >> adj to store the data.
I have modified your program slightly to store all the pair of data related to a node.
#include <iostream>
#include <utility>
#include <list>
#include <fstream>
#include <sstream>
#include <cstdlib>
#include <vector>
using namespace std;
typedef pair<int, int> ipair;
class Graph
{
int V;
vector<list< pair <int, int> >> adj;
public:
bool read_file();
void print_graph();
Graph(int V);
};
Graph::Graph( int V)
{
this-> V = V;
adj.reserve(V);
}
bool Graph:: read_file()
{
const long int N = 1000000;
std::ifstream infile("Dijkstra.txt");
if(!infile.is_open()) return false;
std::string line;
int i = 0;
while ( i < N && getline(infile, line) ) {
std::istringstream str(line);
int u;
str >> u;
if ( u > N ) {
// Problem.
abort();
}
int v;
int w;
char c;
while ( str >> v >> c >> w) {
if (u <= (int)adj.size()) { // index (u-1) exists
auto list = adj[u-1]; // get the existing list
list.push_back(make_pair(v,w)); // add new data
adj[u-1] = list; // store it the same index
} else { // index (u-1) doesn't exist
list<ipair> list; // create a new list
list.push_back(make_pair(v,w)); // store the values
adj.push_back(list); // add it in the vector
}
}
++i;
}
return true;
}
void Graph::print_graph() {
int node = 1;
for (auto& x : adj) {
cout << "from node: " << node++ << endl;
for (auto it = begin(x); it != end(x); ++it) {
cout << it->first << " " << it->second << "\n";
}
}
}
int main() {
Graph g(200);
g.read_file();
g.print_graph();
return 0;
}

You can regex match ,.
e.g.
std::regex pairs_regex("(\d+),(\d+)");
auto pairs_it = std::sregex_iterator(line.begin(), line.end(), pairs_regex);
auto pairs_end = std::sregex_iterator();
for(;pairs_it != pairs_end; ++pairs_it)
{
std::string v = pairs_it->str(1);
std::string w = pairs_it->str(2);
adj[u].push_back(make_pair(std::stoi(v), std::stoi(w)));
}
N.B. it would be safer if adj were a std:: container, not a raw array. The loop body would populate a list<pair<int, int>> temp, then adj.emplace_back(std::move(temp))

Related

Segmentation Fault - unordered_map(tarjan's algorithm)

I have implemented Tarjan's algorithm to find strongly connected component in a graph and getting Segmentation fault for some large input.
#include <iostream>
#include <sstream>
#include <vector>
#include <stack>
#include <unordered_set>
#include <unordered_map>
using namespace std;
class Graph {
public:
int n, m;
unordered_map<int, unordered_set<int>> graph;
Graph(int n, int m): n(n), m(m) {}
void add_edge(int u, int v) {
graph[u].insert(v);
if(graph.find(v) == graph.end()) graph[v] = unordered_set<int>();
}
void scc_helper(int u, unordered_map<int, int>& disc, unordered_map<int, int>& low, stack<int>& st, unordered_map<int, bool>& inStack, vector<unordered_set<int>>& scc, int& time) {
disc[u] = low[u] = time++;
st.push(u);
inStack[u] = true;
for(const int& ch: graph[u]) {
if(disc[ch] == 0) {
scc_helper(ch, disc, low, st, inStack, scc, time);
low[u] = min(low[u], low[ch]);
} else if(inStack[ch]) {
low[u] = min(low[u], disc[ch]);
}
}
if(disc[u] == low[u]) {
scc.push_back(unordered_set<int>());
while(st.top() != u) {
scc.back().insert(st.top());
inStack[st.top()] = false;
st.pop();
}
scc.back().insert(st.top());
inStack[st.top()] = false;
st.pop();
}
};
vector<unordered_set<int>> get_scc() {
unordered_map<int, int> disc, low;
stack<int> st;
unordered_map<int, bool> inStack;
vector<unordered_set<int>> scc;
int time = 1;
for(const auto& p: graph) {
if(disc[p.first] == 0)
scc_helper(p.first, disc, low, st, inStack, scc, time);
}
cerr << "completed" << endl;
return scc;
}
};
void read(string& input) {
do {
getline(cin, input);
} while(input.length() > 0 && input[0] == '%');
}
Graph read_graph() {
string input;
istringstream ss;
int n, m, v;
read(input); ss.str(input);
ss >> n >> m;
ss.clear();
Graph G(n, m);
for(int u=1; u<=n; u++) {
read(input); ss.str(input);
while(ss >> v) {
G.add_edge(u, v);
} ss.clear();
if(G.graph.find(u) == G.graph.end()) G.graph[u] = unordered_set<int>();
}
return G;
}
int main() {
Graph G = read_graph();
cerr << "read input\n";
vector<unordered_set<int>> scc = G.get_scc();
cout << scc.size() << "\n";
}
After debugging, I have found that the condition if(disc[u] == low[u]) is not getting evaluated to true when program throws segmentation fault.
Another thing is that, Segmentation fault was received on lines disc[u] = low[u] = time++;, st.push(u); and inStack[u] = true; and at that time there were approx. 20,000 entries in each of the unordered_map and in stack.
Some details about input:
for, n=16384 m=283794, the program is working correctly
but for, n=58960 m=269439, it is throwing segmentation fault.

adjacency graph in c++ using stl

I want to make adjacency list representation of graph using vector and when we declare vector as global variable where the memory is allocated to vector in stack or heap
#include<bits/stdc++.h>
#include<vector>
using namespace std;
void addedge(vector<int> &graph, int u, int v) {
graph[u].push_back(v);
graph[v].push_back(u);
}
void printgraph(const vector<int> &gph) {
for (int v = 0 : gph) {
cout << v;
for (auto x : gph[v]) {
cout << x;
printf("\n");
}
}
}
int main() {
vector<int> gph;
addedge(gph, 2, 3);
addedge(gph, 6, 7);
addedge(gph, 1, 2);
printgraph(gph);
}
gph is a vector of int so you cannot access the method push_back in graph[u] because graph[u] is a int!
You can imagine an adjacency list as a space efficient matrix(2D) of int where you can have rows of different sizes.
But ultimately it is a 2D structure.
This means you have to declare your adjacency list as a vector<vector<int>>.
The following code should give you some indication on how it works:
#include<iostream>
#include<vector>
using Graph = std::vector<std::vector<int>>;
void addedge(Graph &graph, const int u, const int v) {
graph[u].push_back(v);
graph[v].push_back(u);
}
void printgraph(const Graph &gph) {
for (int node = 0 ; node < gph.size() ; node++) {
std::cout<<node<<" : ";
for (auto x : gph[node]) {
std::cout << x << " ";
}
std::cout<<std::endl;
}
}
int main() {
Graph gph(8, std::vector<int>());
addedge(gph, 2, 3);
addedge(gph, 6, 7);
addedge(gph, 1, 2);
printgraph(gph);
}
Rather than having an explicit parameter of your adjacency list, you could collect the data and behaviour into a class. Depending on how sparse your graph is, there are various representations available. To contrast with Davide's answer, I'll use std::multimap<int, int>.
class Graph
{
std::multimap<int, int> edges;
public:
void addedge(int u, int v)
{
edges.insert(u, v);
edges.insert(v, u);
}
friend std::ostream& operator<<(std::ostream& os, const Graph & graph)
{
for (auto v_it = graph.begin(), v_end = {}; v_it != graph.end(); v_it = v_end)
{
v_end = graph.upper_bound(v_it->first);
os << v_it->first << " : ";
for (auto it = v_it; it != v_end; ++it)
{
os << it->second << " ";
}
os << std::endl;
}
return os;
}
}
int main() {
Graph gph;
gph.addedge(2, 3);
gph.addedge(6, 7);
gph.addedge(1, 2);
std::cout << graph;
}

how to access vector of map of ( int and vector of strings )

how do i access map of int and vectors of string in the passed_vector function.
I just want to print them in that function.
#include <iostream>
#include <vector>
#include <map>
#include <string>
using namespace std;
typedef vector< map< int, vector<string> > > vmis;
typedef map< int, vector<string> > mis;
typedef vector<string> vstr;
void passing_vector(const vmis &meetings);
//return size of vector
template< typename A > size_t n_elements( const A& a )
{
return sizeof a / sizeof a[ 0 ];
}
int main()
{
vmis meeting_info;
mis meeting_members;
vstr sw_vec;
vstr sys_vec;
string sw_team[] = {"Ricky", "John", "David"};
string sys_team[] = {"Simmon", "Brad", "Schmidt", "Fizio"};
sw_vec.insert(sw_vec.begin(), sw_team, sw_team + n_elements(sw_team) );
sys_vec.insert(sys_vec.begin(), sys_team, sys_team + n_elements(sys_team) );
meeting_members.insert(make_pair(520, sw_vec));
meeting_members.insert(make_pair(440, sys_vec));
meeting_info.push_back(meeting_members);
passing_vector(meeting_info);
return 0;
}
void passing_vector(const vmis &meetings)
{
vmis::iterator itvmis = meetings.begin();
//how do i access map of int and vectors of string.
//I just want to print them.
}
I know how to print them in main function.
vmis::iterator itvims = meeting_info.begin();
for( int i = 0; i < meeting_info.size(); i++ )
{
mis::iterator itm = meeting_members.begin();
for(itm; itm != meeting_members.end(); itm++ )
{
cout << itm->first << " : ";
vstr::iterator it = itm->second.begin();
for(it; it != itm->second.end(); it++)
cout << *it << " ";
cout << endl;
}
}
desired output
440 : Simmon Brad Schmidt Fizio
520 : Ricky John David
if there is a better way of doing this suggestions are always welcome.
The easiest aproach is to use auto, also since your meetings is const, you need to use const_iterator:
void passing_vector(const vmis &meetings)
{
vmis::const_iterator itvims = meetings.begin();
//how do i access map of int and vectors of string.
//I just want to print them.
for (;itvims != meetings.end(); ++itvims)
{
const auto& map_item = *itvims;
for (const auto& map_it : map_item)
{
int map_key = map_it.first;
const auto& str_vec = map_it.second;
for (const auto& str : str_vec)
{
std::cout << map_key << " - " << str << "\n";
}
}
}
}
[edit]
c++98 version:
void passing_vector(const vmis &meetings)
{
vmis::const_iterator itvims = meetings.begin();
//how do i access map of int and vectors of string.
//I just want to print them.
for (;itvims != meetings.end(); ++itvims)
{
const mis& map_item = *itvims;
for (mis::const_iterator map_it = map_item.begin(); map_it != map_item.end(); ++map_it)
{
int map_key = map_it->first;
const vstr& str_vec = map_it->second;
for (vstr::const_iterator sitr = str_vec.begin(); sitr != str_vec.end(); ++sitr)
{
std::cout << map_key << " - " << *sitr << "\n";
}
}
}
}

Convert a set to a vector of vector

I'm new to C++ and I'm trying to convert unordered_set<string> to vector<vector<int>>
The set contains ("1,2,2","1","1,2","2","2,2"), and each element is a string.
I would like to output a vector<vector<int>> containing
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
So how would I parse each element (string) and make it looks like the above scheme?
#define SSTR( x ) dynamic_cast< std::ostringstream & >( \
( std::ostringstream() << std::dec << x ) ).str()
vector<vector<int> > subsetsWithDup(const vector<int> &num) {
unordered_set<string> result;
for (int i = 0; i < num.size(); i++)
{
result.insert(SSTR(num[i]));
for (int j = i+1; j < num.size(); j++)
{
string d = SSTR(num[i]) + "," +SSTR(num[j]);
result.insert(d);
}
}
string lastString= "";
for (int i = 0; i < num.size(); i++)
{
if ( i == num.size() -1)
{
lastString+= SSTR(num[i]);
}
else
{
lastString+= SSTR(num[i])+",";
}
}
result.insert(lastString);
// convert result back to vector<vector<int>>
return result;
}
If you don't need to validate your strings you can do a transform to construct the vector<vector<int>>:
set<string> foo{ "1,2,2", "1", "1,2", "2", "2,2" };
vector<vector<int>> bar(foo.size());
transform(foo.begin(), foo.end(), bar.begin(), [](const string& i){
vector<int> result;
auto it = const_cast<char*>(i.c_str());
for (result.push_back(static_cast<int>(strtol(it, &it, 10)));
it < i.c_str() + i.size();
result.push_back(static_cast<int>(strtol(++it, &it, 10))));
return result;
});
The lambda in the transform will step through the string:
Start it at the beginning of the string
Using strtol to extract each number
Pushing each number into result
Stepping over each ',' with ++it
Return the constructed result
Prerequisites: This lambda assumes that your input set doesn't contain:
An empty string
A string that begins or ends with a comma (as in "1,2,")
A string that has consecutive commas (as in "1,,2")
That the string contains only contains digits and commas
Note: As a general rule a const_cast is bad so I wanted to comment on why I'm making one. Note that *it is never written to, only it is written to. So this is not violating the const-ness of const string& i. An alternative to strtol that doesn't require a const_cast is stoi, but until we get string_view from C++14 we'll need to construct a new string at each comma, so that's dreadfully inefficient.
One of the possible solution can be:
vector<string> split (string str, string seq) {
vector<string> ret {};
size_t pos {};
while ((pos = str.find (seq)) != string::npos) {
ret.push_back (str.substr (0, pos));
str = str.substr (pos+seq.size ());
}
ret.push_back (str);
return ret;
}
vector<int> to_vec_int (vector<string>&& vec) {
vector<int> ret {};
for (const auto& v : vec) {
ret.push_back (stoi (v));
}
return ret;
}
int main () {
unordered_set<string> st {"1,2,2","1","1,2","2","2,2"};
vector<vector<int>> vec {};
for (const auto& s : st) {
vec.push_back (to_vec_int (split (s, ",")));
}
for (const auto& v : vec) {
for (const auto& s : v) {
cout << s << " ";
}
cout << endl;
}
return 0;
}
Since your elements are delimited by comma, we can split them using split () function and we get vector of strings. This vector has to be converted to vector of int, which is the reason of existence of to_vec_int ().
Here is another possible solution using istringstream to find the commas:
#include <iostream>
#include <string>
#include <vector>
#include <unordered_set>
#include <sstream>
void main()
{
using namespace std;
unordered_set<string> strSet;
strSet.insert("1,2,2");
strSet.insert("1");
strSet.insert("1,2");
strSet.insert("2");
strSet.insert("2,2");
vector<int> nums;
vector<vector<int>> arr_of_nums;
for (const auto &str : strSet) {
istringstream strStream(str);
string strToInt;
while (getline(strStream, strToInt, ',')) {
nums.push_back(stoi(strToInt));
}
arr_of_nums.push_back(nums);
nums.clear();
}
for(const auto &nums : arr_of_nums) {
for (const auto &num : nums) {
cout << num << ",";
}
cout << endl;
}
}

Combination of lists by type algorithm

I'm attempting to create an algorithm in C++ which will give me all of the possible combinations of a set of list items (input in a map format). I want to avoid duplicates and make sure to cover all possible combinations. To simplify the example, here's what the input may look like:
map<string, vector<string> > sandwichMap;
sandwichMap["bread"].push_back("wheat");
sandwichMap["bread"].push_back("white");
sandwichMap["meat"].push_back("ham");
sandwichMap["meat"].push_back("turkey");
sandwichMap["meat"].push_back("roastbeef");
sandwichMap["veggie"].push_back("lettuce");
sandwichMap["sauce"].push_back("mustard");
I'd feed this map into the algorithm, and it should spit out a vector with all of the possible combinations (using one of each key type):
wheat+ham+lettuce+mustard
wheat+turkey+lettuce+mustard
wheat+roastbeef+lettuce+mustard
white+ham+lettuce+mustard
white+turkey+lettuce+mustard
white+roastbeef+lettuce+mustard
It needs to work for any map of string vectors. So far I've tried and gotten close, but I end up with duplicate combinations and missed combinations:
sandwichList getCombinations(sandwichMap sMap)
{
locList retList;
int totalCombos = 1;
for (sandwichMapIt i = sMap.begin(); i != sMap.end(); ++i)
{
totalCombos *= i->second.size();
}
retList.resize(totalCombos);
int locCount;
for (sandwichMapIt a = sMap.begin(); a != sMap.end(); ++a)
{
locCount = 0;
for (locListIt l = a->second.begin(); l != a->second.end(); ++l)
{
for (unsigned int i = 0; i < totalCombos / a->second.size(); ++i)
{
retList[i + a->second.size() * locCount] += *l;
}
locCount++;
}
}
return retList;
}
Any help would be greatly appreciated!
Updated code:
#include <vector>
#include <map>
#include <list>
#include <iostream>
typedef std::vector<std::string> strVec;
typedef std::list<std::string> strList;
typedef std::map<std::string, strVec> sandwichMap;
int main()
{
sandwichMap sMap;
sMap["bread"].push_back("wheat");
sMap["bread"].push_back("white");
sMap["meat"].push_back("ham");
sMap["meat"].push_back("turkey");
sMap["meat"].push_back("roastbeef");
sMap["veggie"].push_back("lettuce");
sMap["sauce"].push_back("mustard");
strList finalSandwichList;
for (sandwichMap::iterator i = sMap.begin(); i != sMap.end(); ++i)
{
strList tmpSandwich;
for (strVec::iterator j = i->second.begin(); j != i->second.end(); ++j)
{
if (finalSandwichList.empty())
{
tmpSandwich.push_back(*j);
}
else
{
for (strList::iterator k = finalSandwichList.begin(); k != finalSandwichList.end(); ++k)
tmpSandwich.push_back(*k + "+" + *j);
}
}
tmpSandwich.swap(finalSandwichList);
}
for (strList::iterator i = finalSandwichList.begin(); i != finalSandwichList.end(); ++i)
{
std::cout << *i << std::endl;
}
return 0;
}
//solution
std::list<std::string> result;
for(auto i=sandwichMap.begin(); i!=sandwichMap.end(); ++i) {
std::list<std::string> new_result;
for(auto j=i->second.begin(); j!=i->second.end(); ++j) {
if(result.empty())
new_result.push_back(*j);
else
for(auto k=result.begin(); k!=result.end(); ++k)
new_result.push_back(*k + "+" + *j);
}
new_result.swap(result);
}
This should work :
#include<iostream>
#include<map>
#include<string>
#include<algorithm>
using namespace std;
map<string, vector<string>> sMap;
vector<string> add;
int sett[200], countt;
void solve(map<string, vector<string>>::iterator itt, int ct, vector<string> addd){
vector<string> tmp = itt->second;
if(ct == countt){
for(int j=0;j<addd.size();j++){
cout<<addd[j]<<" ";
}
cout<<endl;
return;
}
itt++;
for(int i=0;i<tmp.size();i++){
//cout<<tmp[i]<<" ";
addd.push_back(tmp[i]);
solve(itt, ct+1, addd);
vector<string>::iterator tempIt = addd.end();
addd.erase(tempIt--);
}
}
int main(){
sMap["bre"].push_back("wh");
sMap["bre"].push_back("whi");
sMap["me"].push_back("ham");
sMap["me"].push_back("tur");
sMap["me"].push_back("rr");
sMap["veg"].push_back("let");
sMap["sau"].push_back("mus");
countt = sMap.size();
solve(sMap.begin(), 0, add);
return 0;
}
I have used backtracking to evaluate every possible combination.
Note : it is in c++11 you might need to change some part of the code for lower version of c++
link to output : http://ideone.com/Ou2411
The code is kinda long because of the helper methods, but it does the job:
#include <vector>
#include <string>
#include <map>
#include <iostream>
using namespace std;
template <class T>
vector<T> Head(const vector<T> &v) {
return vector<T>(v.begin(), v.begin() + 1);
}
template <class T>
vector<T> Tail(const vector<T> &v) {
auto first = v.begin() + 1;
auto last = v.end();
return vector<T>(first, last);
}
template <class T>
vector<T> Concat(const vector<T> &v1, const vector<T> &v2) {
vector<T> result = v1;
result.insert(result.end(), v2.begin(), v2.end());
return result;
}
vector<vector<string>> CombineVectorWithScalar(const vector<vector<string>> &v, const string &scalar) {
vector<vector<string>> result = v;
for (unsigned i = 0; i < v.size(); i++) {
result[i].push_back(scalar);
}
return result;
}
vector<vector<string>> CombineVectorWithVector(const vector<vector<string>> &v1, const vector<string> &v2) {
if (v2.empty()) {
return vector<vector<string>>();
}
else {
auto headCombination = CombineVectorWithScalar(v1, v2.front());
auto tailCombination = CombineVectorWithVector(v1, Tail(v2));
return Concat(headCombination, tailCombination);
}
}
vector<string> GetKeys(const map<string, vector<string>> &mp) {
vector<string> keys;
for (auto it = mp.begin(); it != mp.end(); ++it) {
keys.push_back(it->first);
}
return keys;
}
vector<vector<string>> CombineMapValues(const map<string, vector<string>> &mp) {
vector<string> keys = GetKeys(mp);
vector<vector<string>> result;
auto &firstVector = mp.begin()->second;
for (auto it = firstVector.begin(); it != firstVector.end(); ++it) {
vector<string> oneElementList;
oneElementList.push_back(*it);
result.push_back(oneElementList);
}
vector<string> restOfTheKeys = Tail(keys);
for (auto it = restOfTheKeys.begin(); it != restOfTheKeys.end(); ++it) {
auto &currentVector = mp.find(*it)->second;
result = CombineVectorWithVector(result, currentVector);
}
return result;
}
void PrintCombinations(const vector<vector<string>> & allCombinations) {
for (auto it = allCombinations.begin(); it != allCombinations.end(); ++it) {
auto currentCombination = *it;
for (auto itInner = currentCombination.begin(); itInner != currentCombination.end(); ++itInner) {
cout << *itInner << " ";
}
cout << endl;
}
}
int main() {
map<string, vector<string> > sandwichMap;
sandwichMap["bread"].push_back("wheat");
sandwichMap["bread"].push_back("white");
sandwichMap["meat"].push_back("ham");
sandwichMap["meat"].push_back("turkey");
sandwichMap["meat"].push_back("roastbeef");
sandwichMap["veggie"].push_back("lettuce");
sandwichMap["sauce"].push_back("mustard");
auto allCombinations = CombineMapValues(sandwichMap);
PrintCombinations(allCombinations);
return 0;
}
void generate_all(std::map<std::string,std::vector<std::string>>::iterator start,
std::vector<std::string::iterator> accomulator,
std::map<std::string,std::vector<std::string>>& sMap){
for (auto it=start; it!=sMap.end(); ++it){
for (auto jt=it->second.begin(); jt!=it->second.end(); jt++){
generate_all(start+1,accomulator.pus_back[jt],sMap);
}
}
if (accomulator.size() == sMap.size()){
// print accomulator
}
}
Call with generate_all(sMap.begin(),aVector,sMap);
If the map is too big to go recursively, you can always generate an equivalent iterative code.
This solution is not recursive. Basically what it does is the following:
Compute how many combinations are actually possible
Know that for each key in the map, you're going to have to add nrCombinations/nrItemsInKey of them in total.
You can see it as a tree growing, branching more and more the more keys you have visited.
If you keep track of how many there are, how spaced they are and where they start you can automatically fill all combinations.
Code
#include <vector>
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<std::string, std::vector<std::string> > sandwichMap;
sandwichMap["bread"].push_back("wheat");
sandwichMap["bread"].push_back("white");
sandwichMap["meat"].push_back("ham");
sandwichMap["meat"].push_back("turkey");
sandwichMap["meat"].push_back("roastbeef");
sandwichMap["veggie"].push_back("lettuce");
sandwichMap["sauce"].push_back("mustard");
sandwichMap["sauce"].push_back("mayo");
// Compute just how many combinations there are
int combinationNr = 1;
for ( auto it : sandwichMap ) {
combinationNr *= it.second.size();
}
std::vector<std::vector<std::string>> solutions(combinationNr);
// We start with empty lists, thus we only have one cluster
int clusters = 1, clusterSize = combinationNr;
for ( auto category : sandwichMap ) {
int startIndex = 0;
int itemsNr = category.second.size();
int itemsPerCluster = clusterSize / itemsNr;
for ( auto item : category.second ) {
for ( int c = 0; c < clusters; ++c ) {
for ( int i = 0; i < itemsPerCluster; ++i ) {
// We sequentially fill each cluster with this item.
// Each fill starts offset by the quantity of items
// already added in the cluster.
solutions[startIndex+i+c*clusterSize].push_back(item);
}
}
startIndex += itemsPerCluster;
}
clusters *= itemsNr;
clusterSize = combinationNr / clusters;
}
for ( auto list : solutions ) {
for ( auto element : list ) {
std::cout << element << ", ";
}
std::cout << "\n";
}
return 0;
}