-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.cpp
More file actions
253 lines (219 loc) · 9.52 KB
/
main.cpp
File metadata and controls
253 lines (219 loc) · 9.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
#include <iostream>
#include <fstream>
#include <vector>
#include <array>
#include <queue>
#include <map>
#include <limits>
#include <sstream>
#define For(i,a,b) for(int i = a; i < b; i++)
using namespace std;
unsigned long long LLINF = numeric_limits<unsigned long long>::max();
struct AssignmentState
{
int personIndex;
int pts;
int e1;
int e2;
int e3;
int e4;
int e5;
int e6;
unsigned long long aid; // Index of this state in the allstates vector
unsigned long long pid = LLINF; // Index of the best state that led to this state
};
/*** Overloads the comparison operator for the custom AssignmentState struct to be processed in the right order for the priority queue.
* PQ elements will be processed in ascending order based on person index, or in descending order of points if two states are
* for the same person index. ***/
bool operator <(const AssignmentState& x, const AssignmentState& y) {
int ypts = -1*y.pts;
int xpts = -1*x.pts;
return std::tie(x.personIndex, xpts, x.e1, x.e2, x.e3, x.e4, x.e5, x.e6) > std::tie(y.personIndex, ypts, y.e1, y.e2, y.e3, y.e4, y.e5, y.e6);
}
// Overload << operator for debugging
ostream& operator<<(ostream& os, const AssignmentState& dps) {
return os << "<" << dps.personIndex << ", "
<< dps.pts << ", "
<< dps.e1 << ", "
<< dps.e2 << ", "
<< dps.e3 << ", "
<< dps.e4 << ", "
<< dps.e5 << ", "
<< dps.e6 << ">";
}
// Overload the << operator for vectors to easily print vectors in the console.
template < class T >
std::ostream& operator << (std::ostream& os, const std::vector<T>& v)
{
os << "[";
for (auto ii = v.begin(); ii != v.end(); ++ii)
{
os << " " << *ii;
}
os << "]";
return os;
}
int numberOfPeople;
int numberOfEvents = 6;
int eventMax;
int eventMin;
/*** Reads the preference rankings of a team from a comma separated values file. ***/
pair<vector<vector<int>>, vector<string>> readCSV(const string& filepath)
{
ifstream fin(filepath);
string line;
getline(fin,line); // Skip the header line in the file
vector<vector<int>> preferenceArray;
vector<string> names;
while(getline(fin, line))
{
istringstream linestream(line);
preferenceArray.emplace_back(6,0);
for (int i = 0; i <= numberOfEvents; i++)
{
string nextData;
getline(linestream, nextData, ',');
if (i == 0)
names.push_back(nextData);
else
preferenceArray.at(preferenceArray.size()-1).at(i-1) = stoi(nextData);
}
}
return make_pair(preferenceArray, names);
}
vector<vector<int>> eventRankings;
priority_queue<AssignmentState> processingQueue;
vector<AssignmentState> allStates;
int bestAssignmentScore = numeric_limits<int>::min();
vector<int> bestCapacities(numberOfEvents, 0);
unsigned long long bestPrevID = -1; // bestPrevID is the AssignmentState ID number used to match names to events at the end
/*** Adds a person to a team assignment and determines the resulting assignment score and event counts
* input: AssignmentState (person index, assignment score, # of students currently in events 1 to 6)
* output: Updates the processingQueue, allStates, bestAssignmentScore, bestCapacities, and bestPrevID global variables ***/
void processPerson(const AssignmentState& curstate)
{
vector<int> eventCapacity {curstate.e1, curstate.e2, curstate.e3, curstate.e4, curstate.e5, curstate.e6};
// Iterate through all possible event selections for current person
for (int possibleEvent = 0; possibleEvent < numberOfEvents; possibleEvent++)
{
// Ensure that event capacity is within limit if this person takes possibleEvent
if (eventCapacity.at(possibleEvent) + 1 > eventMax)
break;
// Add points for this person taking event in consideration
int addedPts = 0;
addedPts += eventRankings.at(curstate.personIndex).at(possibleEvent);
// Determine the new event counts if this person takes possibleEvent
int newE1 = curstate.e1 + (possibleEvent == 0 ? 1 : 0);
int newE2 = curstate.e2 + (possibleEvent == 1 ? 1 : 0);
int newE3 = curstate.e3 + (possibleEvent == 2 ? 1 : 0);
int newE4 = curstate.e4 + (possibleEvent == 3 ? 1 : 0);
int newE5 = curstate.e5 + (possibleEvent == 4 ? 1 : 0);
int newE6 = curstate.e6 + (possibleEvent == 5 ? 1 : 0);
AssignmentState newState {curstate.personIndex + 1, curstate.pts + addedPts,
newE1, newE2, newE3, newE4, newE5, newE6, allStates.size(), curstate.aid};
// The allstates vector is used later to match the names of the people with their events
allStates.push_back(newState);
if (curstate.personIndex < numberOfPeople - 1) {
// Add this state to the priority queue to continue exploring if there are more people to process
processingQueue.emplace(newState);
}
else if (curstate.pts + addedPts > bestAssignmentScore
&& min({newE1, newE2, newE3, newE4, newE5, newE6}) >= eventMin) {
// Mark this state as the best if it has the highest score and meets the event capacity criteria
bestAssignmentScore = curstate.pts + addedPts;
bestCapacities = {newE1, newE2, newE3, newE4, newE5, newE6 };
bestPrevID = newState.aid;
}
}
}
int main() {
/*** INPUT: Reading information from CSV file ***/
cout << "Enter filepath of CSV data file to read: " << endl;
string inputFilepath;
getline(cin, inputFilepath);
pair<vector<vector<int>>, vector<string>> csvdata = readCSV(inputFilepath);
eventRankings = csvdata.first;
vector<string> names = csvdata.second;
numberOfPeople = eventRankings.size();
eventMax = (numberOfPeople / numberOfEvents) + 1;
eventMin = (numberOfPeople / numberOfEvents) - 1;
bool ******visited = (bool******)malloc((eventMax+1)*sizeof(bool*****)); //[eventMax+1][eventMax+1][eventMax+1][eventMax+1][eventMax+1][eventMax+1];
For(i, 0, eventMax+2)
{
visited[i] = (bool*****)malloc((eventMax+1)*sizeof(bool****));
For(j, 0, eventMax+2)
{
visited[i][j] = (bool****)malloc((eventMax+1)*sizeof(bool***));
For(k, 0, eventMax+2)
{
visited[i][j][k] = (bool***)malloc((eventMax+1)*sizeof(bool**));
For(l, 0, eventMax+2)
{
visited[i][j][k][l] = (bool**)malloc((eventMax+1)*sizeof(bool*));
For(m, 0, eventMax+2)
{
visited[i][j][k][l][m] = (bool*)malloc((eventMax+1)*sizeof(bool));
For(n, 0, eventMax+2)
{
visited[i][j][k][l][m][n] = false;
}
}
}
}
}
}
/** The program will attempt to maximize the score, but inputs are given where #1 is best, #2 is worse, etc..
* Therefore, the input scores are multiplied by -1 so the program can correctly "maximize" the score. **/
bool findMin = true;
if (findMin)
For(i, 0, eventRankings.size())
For (j, 0, eventRankings.at(i).size())
eventRankings.at(i).at(j) *= -1;
// Initial state (first person with no events selected)
AssignmentState starting {0, 0, 0, 0, 0, 0, 0, 0, 0, LLINF};
processingQueue.emplace(starting);
allStates.push_back(starting);
/** MAIN LOOP **/
int lastPersonProcessed = -1;
while (!processingQueue.empty())
{
AssignmentState curstate = processingQueue.top();
processingQueue.pop();
// Reset the visited array once reaching a new person
if (lastPersonProcessed < curstate.personIndex)
{
lastPersonProcessed = curstate.personIndex;
fill(&visited[0][0][0][0][0][0], &visited[0][0][0][0][0][0] + sizeof(visited), false);
}
// Make sure to not process a state multiple times
if (visited[curstate.e1][curstate.e2][curstate.e3][curstate.e4][curstate.e5][curstate.e6])
continue;
visited[curstate.e1][curstate.e2][curstate.e3][curstate.e4][curstate.e5][curstate.e6] = true;
processPerson(curstate);
}
cout << bestAssignmentScore * (findMin ? -1 : 1) << endl;
cout << bestCapacities << endl;
/*** OUTPUT ***/
cout << endl << "Assignment list:" << endl;
while(bestPrevID != LLINF)
{
// Retrieve the previous state based on the ID
AssignmentState* bestPrev = &allStates.at(bestPrevID);
AssignmentState* prev = nullptr;
if (bestPrev-> pid != LLINF)
prev = &allStates.at(bestPrev->pid);
// Deduce the events that were selected for the current person based on the changes in event counts
vector<int> selectedEvents;
vector<int> curEventCapacity = {bestPrev->e1, bestPrev->e2, bestPrev->e3, bestPrev->e4, bestPrev->e5, bestPrev->e6};
vector<int> prevEventCapacity;
if (bestPrev->pid == LLINF) break;
else prevEventCapacity = {prev->e1, prev->e2, prev->e3, prev->e4, prev->e5, prev->e6};
For(i, 0, numberOfEvents)
if (curEventCapacity.at(i) > prevEventCapacity.at(i))
selectedEvents.push_back(i+1);
// Print to console
cout << names.at(bestPrev->personIndex - 1) << ": " << selectedEvents << endl;
bestPrevID = bestPrev->pid;
}
cout << "Done!" << endl;
}