| #include <bits/stdc++.h> |
| using namespace std; |
|
|
| using ll = long long; |
|
|
| struct Category { |
| string name; |
| ll q, v, m, l; |
| }; |
|
|
| struct Chunk { |
| int orig; |
| ll take; |
| int wm, wl; |
| ll value; |
| }; |
|
|
| struct Parser { |
| string s; |
| size_t pos = 0; |
| Parser(const string& str) : s(str), pos(0) {} |
| void skipWS() { |
| while (pos < s.size() && isspace((unsigned char)s[pos])) pos++; |
| } |
| bool nextChar(char c) { |
| skipWS(); |
| if (pos < s.size() && s[pos] == c) { pos++; return true; } |
| return false; |
| } |
| string nextString() { |
| |
| while (pos < s.size() && s[pos] != '"') pos++; |
| if (pos >= s.size()) return ""; |
| pos++; |
| string res; |
| while (pos < s.size()) { |
| char ch = s[pos++]; |
| if (ch == '\\') { |
| if (pos < s.size()) { |
| char esc = s[pos++]; |
| |
| if (esc == '"' || esc == '\\' || esc == '/') res.push_back(esc); |
| else if (esc == 'b') res.push_back('\b'); |
| else if (esc == 'f') res.push_back('\f'); |
| else if (esc == 'n') res.push_back('\n'); |
| else if (esc == 'r') res.push_back('\r'); |
| else if (esc == 't') res.push_back('\t'); |
| else res.push_back(esc); |
| } |
| } else if (ch == '"') { |
| break; |
| } else { |
| res.push_back(ch); |
| } |
| } |
| return res; |
| } |
| ll nextInt() { |
| |
| while (pos < s.size() && !(s[pos] == '-' || (s[pos] >= '0' && s[pos] <= '9'))) pos++; |
| bool neg = false; |
| if (pos < s.size() && s[pos] == '-') { neg = true; pos++; } |
| ll val = 0; |
| while (pos < s.size() && s[pos] >= '0' && s[pos] <= '9') { |
| val = val * 10 + (s[pos] - '0'); |
| pos++; |
| } |
| return neg ? -val : val; |
| } |
| }; |
|
|
| static const ll M_CAP = 20000000; |
| static const ll L_CAP = 25000000; |
|
|
| struct Solution { |
| vector<ll> cnt; |
| ll value = 0; |
| ll msum = 0; |
| ll lsum = 0; |
| }; |
|
|
| static inline void computeTotals(const vector<Category>& items, const vector<ll>& cnt, ll& val, ll& msum, ll& lsum) { |
| val = 0; msum = 0; lsum = 0; |
| int n = (int)items.size(); |
| for (int i = 0; i < n; ++i) { |
| if (cnt[i] <= 0) continue; |
| val += cnt[i] * items[i].v; |
| msum += cnt[i] * items[i].m; |
| lsum += cnt[i] * items[i].l; |
| } |
| } |
|
|
| static inline Solution makeSolution(const vector<Category>& items, const vector<ll>& cnt) { |
| Solution s; |
| s.cnt = cnt; |
| computeTotals(items, cnt, s.value, s.msum, s.lsum); |
| return s; |
| } |
|
|
| static inline vector<ll> greedy_pack(const vector<Category>& items, double t, const vector<ll>& qcap) { |
| int n = (int)items.size(); |
| vector<int> idx(n); |
| for (int i = 0; i < n; ++i) idx[i] = i; |
| double M = (double)M_CAP, L = (double)L_CAP; |
| vector<double> dens(n, 0.0); |
| for (int i = 0; i < n; ++i) { |
| if (items[i].m > M_CAP || items[i].l > L_CAP) { dens[i] = -1; continue; } |
| double denom = t * ((double)items[i].m / M) + (1.0 - t) * ((double)items[i].l / L); |
| if (denom <= 0) dens[i] = 0; |
| else dens[i] = (double)items[i].v / denom; |
| } |
| sort(idx.begin(), idx.end(), [&](int a, int b){ |
| if (dens[a] != dens[b]) return dens[a] > dens[b]; |
| return items[a].v > items[b].v; |
| }); |
| vector<ll> cnt(n, 0); |
| ll remM = M_CAP, remL = L_CAP; |
| for (int it : idx) { |
| if (items[it].m > remM || items[it].l > remL) continue; |
| ll can = min(qcap[it], min(remM / items[it].m, remL / items[it].l)); |
| if (can <= 0) continue; |
| cnt[it] = can; |
| remM -= can * items[it].m; |
| remL -= can * items[it].l; |
| } |
| return cnt; |
| } |
|
|
| static inline void fill_leftover(vector<ll>& cnt, const vector<Category>& items, const vector<ll>& qcap) { |
| ll usedM = 0, usedL = 0, val = 0; |
| computeTotals(items, cnt, val, usedM, usedL); |
| ll remM = M_CAP - usedM; |
| ll remL = L_CAP - usedL; |
| if (remM <= 0 || remL <= 0) return; |
| double normM = (double)remM / (double)M_CAP; |
| double normL = (double)remL / (double)L_CAP; |
| double t = (normM + normL > 0) ? (normM / (normM + normL)) : 0.5; |
| int n = (int)items.size(); |
| vector<int> idx(n); |
| for (int i = 0; i < n; ++i) idx[i] = i; |
| double M = (double)M_CAP, L = (double)L_CAP; |
| vector<double> dens(n); |
| for (int i = 0; i < n; ++i) { |
| double denom = t * ((double)items[i].m / M) + (1.0 - t) * ((double)items[i].l / L); |
| dens[i] = (denom > 0) ? (double)items[i].v / denom : 0; |
| } |
| sort(idx.begin(), idx.end(), [&](int a, int b){ |
| if (dens[a] != dens[b]) return dens[a] > dens[b]; |
| return items[a].v > items[b].v; |
| }); |
| for (int it : idx) { |
| if (items[it].m > remM || items[it].l > remL) continue; |
| ll want = qcap[it] - cnt[it]; |
| if (want <= 0) continue; |
| ll can = min(want, min(remM / items[it].m, remL / items[it].l)); |
| if (can <= 0) continue; |
| cnt[it] += can; |
| remM -= can * items[it].m; |
| remL -= can * items[it].l; |
| if (remM <= 0 || remL <= 0) break; |
| } |
| } |
|
|
| static inline vector<ll> dp_solution(const vector<Category>& items, const vector<ll>& qcap) { |
| |
| const int S_M = 100000; |
| const int S_L = 100000; |
| const int Tm = (int)(M_CAP / S_M); |
| const int Tl = (int)(L_CAP / S_L); |
| vector<Chunk> chunks; |
| chunks.reserve(256); |
| int n = (int)items.size(); |
| for (int i = 0; i < n; ++i) { |
| ll qeff = qcap[i]; |
| if (qeff <= 0) continue; |
| ll k = 1; |
| while (qeff > 0) { |
| ll take = min(k, qeff); |
| |
| ll wm_ll = (items[i].m * take + S_M - 1) / S_M; |
| ll wl_ll = (items[i].l * take + S_L - 1) / S_L; |
| if (wm_ll <= Tm && wl_ll <= Tl) { |
| Chunk ch; |
| ch.orig = i; |
| ch.take = take; |
| ch.wm = (int)wm_ll; |
| ch.wl = (int)wl_ll; |
| ch.value = items[i].v * take; |
| chunks.push_back(ch); |
| } |
| qeff -= take; |
| k <<= 1; |
| } |
| } |
| int W = Tl + 1; |
| vector<ll> dp((Tm + 1) * (Tl + 1), 0); |
| vector<int> from((Tm + 1) * (Tl + 1), -1); |
| for (int id = 0; id < (int)chunks.size(); ++id) { |
| int wm = chunks[id].wm; |
| int wl = chunks[id].wl; |
| ll val = chunks[id].value; |
| for (int tm = Tm; tm >= wm; --tm) { |
| int base = tm * W; |
| int prevBase = (tm - wm) * W; |
| for (int tl = Tl; tl >= wl; --tl) { |
| ll cand = dp[prevBase + (tl - wl)] + val; |
| int idx = base + tl; |
| if (cand > dp[idx]) { |
| dp[idx] = cand; |
| from[idx] = id; |
| } |
| } |
| } |
| } |
| |
| vector<ll> cnt(n, 0); |
| int tm = Tm, tl = Tl; |
| int idx = tm * W + tl; |
| while (idx >= 0 && from[idx] != -1) { |
| int id = from[idx]; |
| cnt[chunks[id].orig] += chunks[id].take; |
| tm -= chunks[id].wm; |
| tl -= chunks[id].wl; |
| if (tm < 0 || tl < 0) break; |
| idx = tm * W + tl; |
| } |
| |
| for (int i = 0; i < n; ++i) if (cnt[i] > qcap[i]) cnt[i] = qcap[i]; |
| return cnt; |
| } |
|
|
| |
| static inline bool try_add_by_removal(int i, vector<ll>& cnt, ll& remM, ll& remL, ll& totalVal, const vector<Category>& items, const vector<ll>& qcap) { |
| if (cnt[i] >= qcap[i]) return false; |
| ll mi = items[i].m, li = items[i].l, vi = items[i].v; |
| ll needM = mi > remM ? (mi - remM) : 0; |
| ll needL = li > remL ? (li - remL) : 0; |
| if (needM == 0 && needL == 0) { |
| cnt[i] += 1; |
| remM -= mi; |
| remL -= li; |
| totalVal += vi; |
| return true; |
| } |
| |
| int n = (int)items.size(); |
| vector<int> cand; |
| cand.reserve(n); |
| for (int j = 0; j < n; ++j) { |
| if (cnt[j] > 0) cand.push_back(j); |
| } |
| if (cand.empty()) return false; |
| double dM = (double)needM / (double)M_CAP; |
| double dL = (double)needL / (double)L_CAP; |
| vector<pair<double,int>> order; |
| order.reserve(cand.size()); |
| for (int j : cand) { |
| double denom = dM * (double)items[j].m + dL * (double)items[j].l; |
| if (denom <= 0) denom = 1e-18; |
| double score = (double)items[j].v / denom; |
| order.emplace_back(score, j); |
| } |
| sort(order.begin(), order.end(), [&](const pair<double,int>& a, const pair<double,int>& b){ |
| if (a.first != b.first) return a.first < b.first; |
| return items[a.second].v < items[b.second].v; |
| }); |
| ll freedM = 0, freedL = 0, lostVal = 0; |
| vector<ll> removed(n, 0); |
| for (auto &pr : order) { |
| if (freedM >= needM && freedL >= needL) break; |
| int j = pr.second; |
| if (cnt[j] <= 0) continue; |
| ll avail = cnt[j] - removed[j]; |
| if (avail <= 0) continue; |
| ll needM_rem = needM > freedM ? (needM - freedM) : 0; |
| ll needL_rem = needL > freedL ? (needL - freedL) : 0; |
| ll rM = needM_rem == 0 ? 0 : ( (needM_rem + items[j].m - 1) / items[j].m ); |
| ll rL = needL_rem == 0 ? 0 : ( (needL_rem + items[j].l - 1) / items[j].l ); |
| ll r = max(rM, rL); |
| if (r <= 0) continue; |
| if (r > avail) r = avail; |
| freedM += r * items[j].m; |
| freedL += r * items[j].l; |
| lostVal += r * items[j].v; |
| removed[j] += r; |
| } |
| if (freedM >= needM && freedL >= needL && lostVal < vi) { |
| for (int j = 0; j < n; ++j) { |
| if (removed[j] > 0) { |
| cnt[j] -= removed[j]; |
| remM += removed[j] * items[j].m; |
| remL += removed[j] * items[j].l; |
| totalVal -= removed[j] * items[j].v; |
| } |
| } |
| cnt[i] += 1; |
| remM -= mi; |
| remL -= li; |
| totalVal += vi; |
| return true; |
| } |
| return false; |
| } |
|
|
| static inline void local_improve(vector<ll>& cnt, const vector<Category>& items, const vector<ll>& qcap) { |
| ll val=0, usedM=0, usedL=0; |
| computeTotals(items, cnt, val, usedM, usedL); |
| ll remM = M_CAP - usedM; |
| ll remL = L_CAP - usedL; |
| if (remM < 0 || remL < 0) return; |
| |
| fill_leftover(cnt, items, qcap); |
| computeTotals(items, cnt, val, usedM, usedL); |
| remM = M_CAP - usedM; |
| remL = L_CAP - usedL; |
|
|
| int n = (int)items.size(); |
| |
| vector<int> order(n); |
| for (int i = 0; i < n; ++i) order[i] = i; |
| auto density = [&](int i)->double{ |
| double dm = (double)items[i].m / (double)M_CAP; |
| double dl = (double)items[i].l / (double)L_CAP; |
| double denom = dm + dl; |
| if (denom <= 0) denom = 1e-18; |
| return (double)items[i].v / denom; |
| }; |
| sort(order.begin(), order.end(), [&](int a, int b){ |
| double da = density(a), db = density(b); |
| if (da != db) return da > db; |
| return items[a].v > items[b].v; |
| }); |
| int maxPasses = 3; |
| int operations_limit = 200; |
| for (int pass = 0; pass < maxPasses && operations_limit > 0; ++pass) { |
| bool improved = false; |
| for (int idx = 0; idx < n && operations_limit > 0; ++idx) { |
| int i = order[idx]; |
| |
| int inner = 0; |
| while (cnt[i] < qcap[i] && operations_limit > 0 && inner < 5) { |
| bool ok = try_add_by_removal(i, cnt, remM, remL, val, items, qcap); |
| if (!ok) break; |
| improved = true; |
| operations_limit--; |
| inner++; |
| } |
| } |
| if (!improved) break; |
| } |
| |
| fill_leftover(cnt, items, qcap); |
| } |
|
|
| int main() { |
| ios::sync_with_stdio(false); |
| cin.tie(nullptr); |
| string input((istreambuf_iterator<char>(cin)), istreambuf_iterator<char>()); |
| Parser parser(input); |
| vector<Category> items; |
| vector<string> names; |
| |
| |
| |
| |
| while (true) { |
| |
| string key = parser.nextString(); |
| if (key.empty()) break; |
| |
| ll q = parser.nextInt(); |
| ll v = parser.nextInt(); |
| ll m = parser.nextInt(); |
| ll l = parser.nextInt(); |
| Category c; |
| c.name = key; |
| c.q = q; |
| c.v = v; |
| c.m = m; |
| c.l = l; |
| items.push_back(c); |
| names.push_back(key); |
| |
| |
| } |
| int n = (int)items.size(); |
| if (n == 0) { |
| |
| cout << "{"; |
| bool first = true; |
| for (auto &nm : names) { |
| if (!first) cout << ", "; |
| first = false; |
| cout << " \"" << nm << "\": 0"; |
| } |
| cout << " }"; |
| return 0; |
| } |
| |
| vector<ll> qcap(n); |
| for (int i = 0; i < n; ++i) { |
| ll maxByM = (items[i].m > 0) ? (M_CAP / items[i].m) : 0; |
| ll maxByL = (items[i].l > 0) ? (L_CAP / items[i].l) : 0; |
| ll cap = min(items[i].q, min(maxByM, maxByL)); |
| if (items[i].m > M_CAP || items[i].l > L_CAP) cap = 0; |
| qcap[i] = max(0LL, cap); |
| } |
|
|
| |
| Solution best; |
| best.cnt.assign(n, 0); |
| best.value = 0; |
| best.msum = 0; |
| best.lsum = 0; |
|
|
| auto consider = [&](const vector<ll>& cnt) { |
| Solution s = makeSolution(items, cnt); |
| if (s.msum <= M_CAP && s.lsum <= L_CAP && s.value > best.value) { |
| best = s; |
| } |
| }; |
|
|
| |
| { |
| vector<ll> cnt = dp_solution(items, qcap); |
| |
| fill_leftover(cnt, items, qcap); |
| consider(cnt); |
| } |
|
|
| |
| vector<double> tvals; |
| for (int k = 0; k <= 10; ++k) tvals.push_back(k / 10.0); |
| |
| tvals.push_back(0.33); |
| tvals.push_back(0.67); |
| for (double t : tvals) { |
| vector<ll> cnt = greedy_pack(items, t, qcap); |
| consider(cnt); |
| } |
|
|
| |
| vector<ll> improved = best.cnt; |
| local_improve(improved, items, qcap); |
| consider(improved); |
|
|
| |
| cout << "{\n"; |
| for (int i = 0; i < n; ++i) { |
| cout << " \"" << items[i].name << "\": " << best.cnt[i]; |
| if (i + 1 < n) cout << ",\n"; |
| else cout << "\n"; |
| } |
| cout << "}"; |
| return 0; |
| } |