| #include <bits/stdc++.h> |
| using namespace std; |
|
|
| static const long long CAP_M = 20LL * 1000 * 1000; |
| static const long long CAP_L = 25LL * 1000 * 1000; |
|
|
| struct Item { |
| string name; |
| int q; |
| long long v, m, l; |
| }; |
|
|
| struct Solution { |
| vector<int> cnt; |
| long long usedM = 0, usedL = 0; |
| long long value = 0; |
| }; |
|
|
| struct Parser { |
| string s; |
| size_t p = 0; |
|
|
| explicit Parser(string in) : s(std::move(in)) {} |
|
|
| void skipws() { |
| while (p < s.size() && (unsigned char)s[p] <= ' ') p++; |
| } |
|
|
| bool consume(char c) { |
| skipws(); |
| if (p < s.size() && s[p] == c) { p++; return true; } |
| return false; |
| } |
|
|
| void expect(char c) { |
| skipws(); |
| if (p >= s.size() || s[p] != c) { |
| |
| cerr << "Parse error: expected '" << c << "' at " << p << "\n"; |
| exit(1); |
| } |
| p++; |
| } |
|
|
| string parseString() { |
| skipws(); |
| expect('"'); |
| string out; |
| while (p < s.size() && s[p] != '"') { |
| out.push_back(s[p++]); |
| } |
| expect('"'); |
| return out; |
| } |
|
|
| long long parseInt() { |
| skipws(); |
| bool neg = false; |
| if (p < s.size() && s[p] == '-') { neg = true; p++; } |
| if (p >= s.size() || !isdigit((unsigned char)s[p])) { |
| cerr << "Parse error: expected int at " << p << "\n"; |
| exit(1); |
| } |
| long long x = 0; |
| while (p < s.size() && isdigit((unsigned char)s[p])) { |
| x = x * 10 + (s[p] - '0'); |
| p++; |
| } |
| return neg ? -x : x; |
| } |
|
|
| array<long long,4> parseArray4() { |
| skipws(); |
| expect('['); |
| array<long long,4> a{}; |
| for (int i = 0; i < 4; i++) { |
| a[i] = parseInt(); |
| skipws(); |
| if (i < 3) expect(','); |
| } |
| skipws(); |
| expect(']'); |
| return a; |
| } |
|
|
| vector<Item> parseObjectItemsInOrder() { |
| skipws(); |
| expect('{'); |
| vector<Item> items; |
| skipws(); |
| if (consume('}')) return items; |
| while (true) { |
| string key = parseString(); |
| skipws(); |
| expect(':'); |
| auto arr = parseArray4(); |
| Item it; |
| it.name = key; |
| it.q = (int)arr[0]; |
| it.v = arr[1]; |
| it.m = arr[2]; |
| it.l = arr[3]; |
| items.push_back(it); |
|
|
| skipws(); |
| if (consume('}')) break; |
| expect(','); |
| } |
| return items; |
| } |
| }; |
|
|
| static inline long long clampLL(long long x, long long lo, long long hi) { |
| if (x < lo) return lo; |
| if (x > hi) return hi; |
| return x; |
| } |
|
|
| static inline long long ceil_div(long long a, long long b) { |
| if (a <= 0) return 0; |
| return (a + b - 1) / b; |
| } |
|
|
| static inline long double densityWeighted(const Item& it, long double beta) { |
| long double dm = (long double)it.m / (long double)CAP_M; |
| long double dl = (long double)it.l / (long double)CAP_L; |
| long double denom = beta * dm + (1.0L - beta) * dl; |
| if (denom <= 0) denom = 1e-18L; |
| return (long double)it.v / denom; |
| } |
|
|
| static void recompute(Solution& sol, const vector<Item>& items) { |
| sol.usedM = sol.usedL = 0; |
| __int128 val = 0; |
| for (int i = 0; i < (int)items.size(); i++) { |
| long long c = sol.cnt[i]; |
| sol.usedM += c * items[i].m; |
| sol.usedL += c * items[i].l; |
| val += (__int128)c * (__int128)items[i].v; |
| } |
| sol.value = (long long)val; |
| } |
|
|
| static void fillBest(Solution& sol, const vector<Item>& items, const vector<int>& addOrder) { |
| for (int idx : addOrder) { |
| const auto& it = items[idx]; |
| int remQ = it.q - sol.cnt[idx]; |
| if (remQ <= 0) continue; |
| long long canM = (CAP_M - sol.usedM) / it.m; |
| long long canL = (CAP_L - sol.usedL) / it.l; |
| long long add = min<long long>(remQ, min(canM, canL)); |
| if (add <= 0) continue; |
| sol.cnt[idx] += (int)add; |
| sol.usedM += add * it.m; |
| sol.usedL += add * it.l; |
| sol.value += add * it.v; |
| } |
| } |
|
|
| static void localImproveWeighted(Solution& sol, const vector<Item>& items, long double beta) { |
| int n = (int)items.size(); |
| vector<int> idxs(n); |
| iota(idxs.begin(), idxs.end(), 0); |
|
|
| vector<long double> den(n); |
| for (int i = 0; i < n; i++) den[i] = densityWeighted(items[i], beta); |
|
|
| vector<int> addOrder = idxs; |
| sort(addOrder.begin(), addOrder.end(), [&](int a, int b){ |
| if (den[a] != den[b]) return den[a] > den[b]; |
| return items[a].v > items[b].v; |
| }); |
|
|
| vector<int> removeOrder = idxs; |
| sort(removeOrder.begin(), removeOrder.end(), [&](int a, int b){ |
| if (den[a] != den[b]) return den[a] < den[b]; |
| return items[a].v < items[b].v; |
| }); |
|
|
| |
| if (sol.usedM > CAP_M || sol.usedL > CAP_L) { |
| |
| for (int j : removeOrder) { |
| if (sol.usedM <= CAP_M && sol.usedL <= CAP_L) break; |
| if (sol.cnt[j] <= 0) continue; |
| const auto& it = items[j]; |
| long long overM = max(0LL, sol.usedM - CAP_M); |
| long long overL = max(0LL, sol.usedL - CAP_L); |
| long long needM = ceil_div(overM, it.m); |
| long long needL = ceil_div(overL, it.l); |
| long long rem = max(needM, needL); |
| rem = min<long long>(rem, sol.cnt[j]); |
| if (rem <= 0) continue; |
| sol.cnt[j] -= (int)rem; |
| sol.usedM -= rem * it.m; |
| sol.usedL -= rem * it.l; |
| sol.value -= rem * it.v; |
| } |
| } |
|
|
| const int MAX_STEPS = 2500; |
| vector<long long> bestRem(n), remTmp(n); |
|
|
| for (int step = 0; step < MAX_STEPS; step++) { |
| fillBest(sol, items, addOrder); |
|
|
| long long freeM = CAP_M - sol.usedM; |
| long long freeL = CAP_L - sol.usedL; |
|
|
| long long bestGain = 0; |
| int bestAdd = -1; |
| fill(bestRem.begin(), bestRem.end(), 0); |
|
|
| for (int i = 0; i < n; i++) { |
| const auto& addIt = items[i]; |
| if (sol.cnt[i] >= addIt.q) continue; |
|
|
| long long reqM = max(0LL, addIt.m - freeM); |
| long long reqL = max(0LL, addIt.l - freeL); |
|
|
| long long gain = 0; |
| fill(remTmp.begin(), remTmp.end(), 0); |
|
|
| if (reqM == 0 && reqL == 0) { |
| gain = addIt.v; |
| } else { |
| long long defM = reqM; |
| long long defL = reqL; |
| __int128 lost = 0; |
|
|
| for (int j : removeOrder) { |
| if (j == i) continue; |
| int have = sol.cnt[j]; |
| if (have <= 0) continue; |
| const auto& remIt = items[j]; |
|
|
| long long needM = ceil_div(defM, remIt.m); |
| long long needL = ceil_div(defL, remIt.l); |
| long long x = max(needM, needL); |
| if (x <= 0) break; |
| x = min<long long>(x, have); |
| if (x <= 0) continue; |
|
|
| remTmp[j] = x; |
| lost += (__int128)x * (__int128)remIt.v; |
| defM -= x * remIt.m; |
| defL -= x * remIt.l; |
| if (defM <= 0 && defL <= 0) break; |
| } |
| if (defM > 0 || defL > 0) continue; |
| __int128 net = (__int128)addIt.v - lost; |
| if (net <= 0) continue; |
| gain = (long long)net; |
| } |
|
|
| if (gain > bestGain || (gain == bestGain && bestAdd != -1 && items[i].v > items[bestAdd].v)) { |
| bestGain = gain; |
| bestAdd = i; |
| bestRem = remTmp; |
| } |
| } |
|
|
| if (bestGain <= 0 || bestAdd < 0) break; |
|
|
| |
| for (int j = 0; j < n; j++) { |
| long long r = bestRem[j]; |
| if (r <= 0) continue; |
| const auto& it = items[j]; |
| if (r > sol.cnt[j]) r = sol.cnt[j]; |
| sol.cnt[j] -= (int)r; |
| sol.usedM -= r * it.m; |
| sol.usedL -= r * it.l; |
| sol.value -= r * it.v; |
| } |
| { |
| const auto& it = items[bestAdd]; |
| if (sol.cnt[bestAdd] < it.q && |
| sol.usedM + it.m <= CAP_M && |
| sol.usedL + it.l <= CAP_L) { |
| sol.cnt[bestAdd] += 1; |
| sol.usedM += it.m; |
| sol.usedL += it.l; |
| sol.value += it.v; |
| } else { |
| |
| break; |
| } |
| } |
| } |
|
|
| |
| fillBest(sol, items, addOrder); |
| } |
|
|
| static Solution greedyByAlpha(const vector<Item>& items, long double alpha) { |
| int n = (int)items.size(); |
| vector<int> idxs(n); |
| iota(idxs.begin(), idxs.end(), 0); |
|
|
| vector<long double> score(n); |
| for (int i = 0; i < n; i++) { |
| long double dm = (long double)items[i].m / (long double)CAP_M; |
| long double dl = (long double)items[i].l / (long double)CAP_L; |
| long double denom = alpha * dm + (1.0L - alpha) * dl; |
| if (denom <= 0) denom = 1e-18L; |
| score[i] = (long double)items[i].v / denom; |
| } |
|
|
| sort(idxs.begin(), idxs.end(), [&](int a, int b){ |
| if (score[a] != score[b]) return score[a] > score[b]; |
| return items[a].v > items[b].v; |
| }); |
|
|
| Solution sol; |
| sol.cnt.assign(n, 0); |
| for (int idx : idxs) { |
| const auto& it = items[idx]; |
| long long canM = (CAP_M - sol.usedM) / it.m; |
| long long canL = (CAP_L - sol.usedL) / it.l; |
| long long add = min<long long>(it.q, min(canM, canL)); |
| if (add <= 0) continue; |
| sol.cnt[idx] = (int)add; |
| sol.usedM += add * it.m; |
| sol.usedL += add * it.l; |
| sol.value += add * it.v; |
| } |
| return sol; |
| } |
|
|
| static void tryCandidate(const vector<Item>& items, Solution sol, Solution& best) { |
| |
| recompute(sol, items); |
| if (sol.usedM > CAP_M || sol.usedL > CAP_L) return; |
|
|
| static const long double betas[] = {0.15L, 0.35L, 0.5L, 0.65L, 0.85L}; |
| for (long double beta : betas) { |
| Solution t = sol; |
| localImproveWeighted(t, items, beta); |
| if (t.usedM <= CAP_M && t.usedL <= CAP_L && t.value > best.value) best = std::move(t); |
| } |
| if (sol.value > best.value) best = std::move(sol); |
| } |
|
|
| struct HeapSol { |
| long long value; |
| vector<int> cnt; |
| bool operator<(const HeapSol& other) const { return value > other.value; } |
| }; |
|
|
| static void enumerateSubset(const vector<Item>& items, const vector<int>& subset, int T, vector<Solution>& out) { |
| int n = (int)items.size(); |
| vector<int> baseCnt(n, 0); |
|
|
| |
| vector<int> idxs(n); |
| iota(idxs.begin(), idxs.end(), 0); |
| vector<long double> den(n); |
| for (int i = 0; i < n; i++) den[i] = densityWeighted(items[i], 0.5L); |
| sort(idxs.begin(), idxs.end(), [&](int a, int b){ |
| if (den[a] != den[b]) return den[a] > den[b]; |
| return items[a].v > items[b].v; |
| }); |
| vector<int> addOrder = idxs; |
|
|
| priority_queue<HeapSol> pq; |
|
|
| function<void(int, long long, long long, long long)> dfs = [&](int pos, long long usedM, long long usedL, long long val) { |
| if (pos == (int)subset.size()) { |
| Solution sol; |
| sol.cnt = baseCnt; |
| sol.usedM = usedM; |
| sol.usedL = usedL; |
| sol.value = val; |
| fillBest(sol, items, addOrder); |
|
|
| HeapSol hs{sol.value, sol.cnt}; |
| if ((int)pq.size() < T) { |
| pq.push(std::move(hs)); |
| } else if (hs.value > pq.top().value) { |
| pq.pop(); |
| pq.push(std::move(hs)); |
| } |
| return; |
| } |
|
|
| int idx = subset[pos]; |
| const auto& it = items[idx]; |
| int maxFeas = min(it.q, (int)min(CAP_M / it.m, CAP_L / it.l)); |
| |
| maxFeas = min<long long>(maxFeas, min((CAP_M - usedM) / it.m, (CAP_L - usedL) / it.l)); |
| for (int c = 0; c <= maxFeas; c++) { |
| long long nm = usedM + 1LL * c * it.m; |
| long long nl = usedL + 1LL * c * it.l; |
| if (nm > CAP_M || nl > CAP_L) break; |
| long long nv = val + 1LL * c * it.v; |
| baseCnt[idx] = c; |
| dfs(pos + 1, nm, nl, nv); |
| } |
| baseCnt[idx] = 0; |
| }; |
|
|
| dfs(0, 0, 0, 0); |
|
|
| out.clear(); |
| out.reserve(pq.size()); |
| while (!pq.empty()) { |
| auto hs = pq.top(); pq.pop(); |
| Solution sol; |
| sol.cnt = std::move(hs.cnt); |
| recompute(sol, items); |
| out.push_back(std::move(sol)); |
| } |
| } |
|
|
| int main() { |
| ios::sync_with_stdio(false); |
| cin.tie(nullptr); |
|
|
| string input((istreambuf_iterator<char>(cin)), istreambuf_iterator<char>()); |
| Parser parser(input); |
| vector<Item> items = parser.parseObjectItemsInOrder(); |
| int n = (int)items.size(); |
|
|
| Solution best; |
| best.cnt.assign(n, 0); |
| best.usedM = best.usedL = best.value = 0; |
|
|
| |
| { |
| Solution zero; |
| zero.cnt.assign(n, 0); |
| tryCandidate(items, zero, best); |
| } |
|
|
| |
| vector<long double> alphas = {0.0L, 1.0L, 0.5L, 0.25L, 0.75L, 0.1L, 0.9L, 0.33L, 0.67L}; |
| { |
| std::mt19937_64 rng((uint64_t)chrono::high_resolution_clock::now().time_since_epoch().count()); |
| uniform_real_distribution<long double> dist(0.0L, 1.0L); |
| for (int i = 0; i < 50; i++) alphas.push_back(dist(rng)); |
| } |
| for (long double a : alphas) { |
| Solution sol = greedyByAlpha(items, a); |
| tryCandidate(items, sol, best); |
| } |
|
|
| |
| { |
| |
| vector<long double> extra = {0.02L, 0.98L, 0.05L, 0.95L}; |
| for (auto a : extra) { |
| Solution sol = greedyByAlpha(items, a); |
| tryCandidate(items, sol, best); |
| } |
| } |
|
|
| |
| vector<int> idxs(n); |
| iota(idxs.begin(), idxs.end(), 0); |
| vector<int> maxFeas(n); |
| for (int i = 0; i < n; i++) { |
| maxFeas[i] = min(items[i].q, (int)min(CAP_M / items[i].m, CAP_L / items[i].l)); |
| } |
| sort(idxs.begin(), idxs.end(), [&](int a, int b){ |
| if (maxFeas[a] != maxFeas[b]) return maxFeas[a] < maxFeas[b]; |
| return items[a].v > items[b].v; |
| }); |
|
|
| vector<int> subset; |
| long long prod = 1; |
| const long long LEAF_LIMIT = 30000; |
| for (int idx : idxs) { |
| long long next = prod * (long long)(maxFeas[idx] + 1); |
| if ((int)subset.size() >= 5) break; |
| if (next > LEAF_LIMIT) continue; |
| subset.push_back(idx); |
| prod = next; |
| if (prod >= LEAF_LIMIT) break; |
| } |
|
|
| if (!subset.empty()) { |
| vector<Solution> leafSols; |
| enumerateSubset(items, subset, 60, leafSols); |
| for (auto &sol : leafSols) tryCandidate(items, sol, best); |
| } |
|
|
| |
| { |
| std::mt19937_64 rng((uint64_t)chrono::high_resolution_clock::now().time_since_epoch().count() ^ 0x9e3779b97f4a7c15ULL); |
| uniform_int_distribution<int> typeDist(0, max(0, n-1)); |
|
|
| for (int it = 0; it < 220; it++) { |
| Solution s = best; |
| int shakes = 2 + (int)(rng() % 4); |
| for (int k = 0; k < shakes; k++) { |
| int i = typeDist(rng); |
| if (s.cnt[i] <= 0) continue; |
| int mx = max(1, s.cnt[i] / 2); |
| int rem = 1 + (int)(rng() % (unsigned long long)mx); |
| rem = min(rem, s.cnt[i]); |
| s.cnt[i] -= rem; |
| } |
| recompute(s, items); |
| if (s.usedM > CAP_M || s.usedL > CAP_L) continue; |
| tryCandidate(items, s, best); |
| } |
| } |
|
|
| |
| 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 << "}\n"; |
| return 0; |
| } |