/*
*from V3 with some modification to handle large output
*reducing input size
*sorting with comparator that relies on income of jobs, the order of jobs can matter
note: try to modify comparator but the result is still the same
*N <= 200
*luu lai level ma tai do dat duoc maxIncome[bitmask], chi goi dfs khi ma no co the dat dc maxIncome som hon (level nho hon) ~ chia 2 truong hop ro rang:
curIncome > or == maxIncome[bitmask]
*/
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <iostream>
using namespace std;
struct job {
vector<string> names;
int income;
int time;
};
map<string, int> dayToNum;
string day[7] = { "mon","tue","wed","thu","fri","sat","sun" };
map<char, int> timeToNum;
char times[3] = { 'M','A','E' };
int avai = (1 << 21) - 1, maxx = 0, curIncome = 0, szList = 0;
vector<job> jobs;
int list[21];
int maxIncome[(1 << 21)], jobs0[(1 << 21)];
bool taken[(1 << 21)];
int uniqueBitmasks[220];
vector<string> names[(1 << 21)];
int cnt = 0, szUniqueBitmasks = 0;
int xxxx=0;
void printWorkTime(int x) {
for (int i = 0; i < 21; i++) {
int b = x % 2;
x /= 2;
if (b) {
int id, id2;
id = 6 - i / 3;
id2 = 2 - i % 3;
cout << " ";
cout << day[id] << " " << times[id2] << '\n';
}
}
}
void dfs(int x) {
//choose the job
if ((avai&jobs[x].time) == 0) {
avai ^= jobs[x].time;
curIncome += jobs[x].income;
int chk=(curIncome > maxIncome[avai]);
maxIncome[avai] = max(maxIncome[avai], curIncome);
if (x == jobs.size() - 1)
maxx = max(maxx, curIncome);
else
if (chk)
dfs(x + 1);
curIncome -= jobs[x].income;
avai ^= jobs[x].time;
}
//dont choose the job
int chk=(curIncome > maxIncome[avai]);
maxIncome[avai] = max(maxIncome[avai], curIncome);
if (x == jobs.size() - 1)
maxx = max(maxx, curIncome);
else
if (chk || (curIncome==0 && curIncome == maxIncome[avai]))
dfs(x + 1);
}
void printCombinationJobs() {
cnt++;
//cout << "Combo " << cnt << ":\n";
int tmp=1;
for (int i = 0; i < szList; i++) {
//cout << " ";
//for (int j = 0; j < jobs[list[i]].names.size(); j++)
//cout << jobs[list[i]].names[j] << ' ';
tmp*=jobs[list[i]].names.size();
//cout<<i<<": "<<jobs[list[i]].names.size()<<'\n';
//cout << "\n";
//printWorkTime(jobs[list[i]].time);
}
xxxx+=tmp;
//cout<<"=========="<<tmp<<' '<<xxxx<<"\n";
}
void findAll(int x) {
//choose the job
if ((avai&jobs[x].time) == 0) {
avai ^= jobs[x].time;
curIncome += jobs[x].income;
list[szList] = x;
szList++;
if (x == jobs.size() - 1) {
if (curIncome == maxx)
printCombinationJobs();
}
else
if (curIncome == maxIncome[avai])
findAll(x + 1);
szList--;
curIncome -= jobs[x].income;
avai ^= jobs[x].time;
}
//dont choose the job
if (x == jobs.size() - 1) {
if (curIncome == maxx)
printCombinationJobs();
}
else
if (curIncome == maxIncome[avai])
findAll(x + 1);
}
bool opt(job &a, job &b) {
return (a.income > b.income);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
for (int i = 0; i < 7; i++)
dayToNum.insert(make_pair(day[i], i));
for (int i = 0; i < 3; i++)
timeToNum.insert(make_pair(times[i], i));
//read available time
int n;
cin >> n;
for (int i = 0; i < n; i++) {
string st;
cin >> st;
int id = dayToNum[st];
cin >> st;
for (int i = 0; i < st.size(); i++) {
int id2 = timeToNum[st[i]];
int tmp = (1 << (20 - id * 3 - id2));
avai ^= tmp;
}
}
//read jobs
cin >> n;
for (int i = 0; i < n; i++) {
string name, typ;
cin >> name >> typ;
int income, m;
cin >> income >> m;
int time = 0, shifts = 0;
//read date and time
for (int j = 0; j < m; j++) {
string d, st;
cin >> d >> st;
int id = dayToNum[d], id2, tmp;
for (int k = 0; k < st.size(); k++) {
char ch = st[k];
id2 = timeToNum[ch];
tmp = (1 << (20 - id * 3 - id2));
if (typ == "flexible") {
//flex job
if (income > jobs0[tmp]) {
if (!taken[tmp])
taken[tmp] = 1,
uniqueBitmasks[szUniqueBitmasks] = tmp,
szUniqueBitmasks++;
jobs0[tmp] = income;
names[tmp].clear();
names[tmp].push_back(name);
}
else if (income == jobs0[tmp])
names[tmp].push_back(name);
}
else {
shifts++;
time ^= tmp;
}
}
}
//fixed job
if (typ == "fixed")
if (income*shifts > jobs0[time]) {
if (!taken[time])
taken[time] = 1,
uniqueBitmasks[szUniqueBitmasks] = time,
szUniqueBitmasks++;
jobs0[time] = income*shifts;
names[time].clear();
names[time].push_back(name);
}
else if (income*shifts == jobs0[time])
names[time].push_back(name);
}
//add all jobs from jobs0 to jobs
jobs.resize(szUniqueBitmasks);
for (int i = 0; i < szUniqueBitmasks; i++) {
int bitmask = uniqueBitmasks[i];
jobs[i].income = jobs0[bitmask];
jobs[i].names = names[bitmask];
jobs[i].time = bitmask;
}
//sort jobs, for faster runtime
sort(jobs.begin(), jobs.end(), opt);
//find max income
dfs(0);
cout << "Max income = " << maxx << '\n';
//find all combinations which can give max income
findAll(0);
cout<<'\n'<<xxxx;
return 0;
}