| #include <bits/stdc++.h> |
| using namespace std; |
|
|
| static const long long CAP_M = 20000000LL; |
| static const long long CAP_V = 25000000LL; |
|
|
| struct Item { |
| string name; |
| int q; |
| long long v; |
| long long m; |
| long long l; |
| }; |
|
|
| struct Bundle { |
| int type; |
| int cnt; |
| int mU; |
| int vU; |
| long long val; |
| }; |
|
|
| struct State { |
| int mU; |
| int vU; |
| long long val; |
| int prev; |
| int bundle; |
| }; |
|
|
| struct FenwickMax { |
| int n; |
| vector<long long> bit; |
| FenwickMax(int n_ = 0) { init(n_); } |
| void init(int n_) { |
| n = n_; |
| bit.assign(n + 1, 0); |
| } |
| void update(int i, long long v) { |
| for (; i <= n; i += i & -i) bit[i] = max(bit[i], v); |
| } |
| long long query(int i) const { |
| long long r = 0; |
| for (; i > 0; i -= i & -i) r = max(r, bit[i]); |
| return r; |
| } |
| }; |
|
|
| struct Solution { |
| vector<int> cnt; |
| long long value = 0; |
| long long mass = 0; |
| long long vol = 0; |
| }; |
|
|
| static inline void skipWS(const string& s, size_t& i) { |
| while (i < s.size() && (unsigned char)s[i] <= 32) i++; |
| } |
|
|
| static inline bool consumeChar(const string& s, size_t& i, char c) { |
| skipWS(s, i); |
| if (i < s.size() && s[i] == c) { i++; return true; } |
| return false; |
| } |
|
|
| static string parseString(const string& s, size_t& i) { |
| skipWS(s, i); |
| if (i >= s.size() || s[i] != '"') return ""; |
| i++; |
| string out; |
| while (i < s.size()) { |
| char c = s[i++]; |
| if (c == '"') break; |
| if (c == '\\') { |
| if (i >= s.size()) break; |
| char e = s[i++]; |
| if (e == '"' || e == '\\' || e == '/') out.push_back(e); |
| else if (e == 'b') out.push_back('\b'); |
| else if (e == 'f') out.push_back('\f'); |
| else if (e == 'n') out.push_back('\n'); |
| else if (e == 'r') out.push_back('\r'); |
| else if (e == 't') out.push_back('\t'); |
| else if (e == 'u') { |
| |
| for (int k = 0; k < 4 && i < s.size(); k++) i++; |
| out.push_back('?'); |
| } |
| } else { |
| out.push_back(c); |
| } |
| } |
| return out; |
| } |
|
|
| static long long parseInt(const string& s, size_t& i) { |
| skipWS(s, i); |
| long long x = 0; |
| bool neg = false; |
| if (i < s.size() && s[i] == '-') { neg = true; i++; } |
| while (i < s.size() && isdigit((unsigned char)s[i])) { |
| x = x * 10 + (s[i] - '0'); |
| i++; |
| } |
| return neg ? -x : x; |
| } |
|
|
| static bool parseInputJSON(const string& s, vector<Item>& items) { |
| size_t i = 0; |
| skipWS(s, i); |
| if (!consumeChar(s, i, '{')) return false; |
| items.clear(); |
| while (true) { |
| skipWS(s, i); |
| if (i >= s.size()) return false; |
| if (s[i] == '}') { i++; break; } |
| string key = parseString(s, i); |
| if (key.empty()) return false; |
| if (!consumeChar(s, i, ':')) return false; |
| if (!consumeChar(s, i, '[')) return false; |
| long long q = parseInt(s, i); |
| if (!consumeChar(s, i, ',')) return false; |
| long long v = parseInt(s, i); |
| if (!consumeChar(s, i, ',')) return false; |
| long long m = parseInt(s, i); |
| if (!consumeChar(s, i, ',')) return false; |
| long long l = parseInt(s, i); |
| if (!consumeChar(s, i, ']')) return false; |
|
|
| Item it; |
| it.name = key; |
| it.q = (int)q; |
| it.v = v; |
| it.m = m; |
| it.l = l; |
| items.push_back(it); |
|
|
| skipWS(s, i); |
| if (i < s.size() && s[i] == ',') { i++; continue; } |
| if (i < s.size() && s[i] == '}') { i++; break; } |
| } |
| return true; |
| } |
|
|
| static inline long long evalSolution(const vector<Item>& items, const vector<int>& cnt, long long& mass, long long& vol) { |
| long long val = 0; |
| mass = 0; |
| vol = 0; |
| for (int i = 0; i < (int)items.size(); i++) { |
| long long c = cnt[i]; |
| val += c * items[i].v; |
| mass += c * items[i].m; |
| vol += c * items[i].l; |
| } |
| return val; |
| } |
|
|
| static vector<int> pruneFrontier(const vector<State>& pool, vector<int> idx, int capMU, int capVU) { |
| if (idx.empty()) return idx; |
| auto cmp = [&](int a, int b) { |
| const State& A = pool[a]; |
| const State& B = pool[b]; |
| if (A.mU != B.mU) return A.mU < B.mU; |
| if (A.vU != B.vU) return A.vU < B.vU; |
| return A.val > B.val; |
| }; |
| sort(idx.begin(), idx.end(), cmp); |
|
|
| |
| vector<int> uniq; |
| uniq.reserve(idx.size()); |
| int lastM = INT_MIN, lastV = INT_MIN; |
| for (int id : idx) { |
| const State& S = pool[id]; |
| if (S.mU < 0 || S.vU < 0 || S.mU > capMU || S.vU > capVU) continue; |
| if (S.mU != lastM || S.vU != lastV) { |
| uniq.push_back(id); |
| lastM = S.mU; |
| lastV = S.vU; |
| } |
| } |
| if (uniq.empty()) return uniq; |
|
|
| |
| vector<int> intra; |
| intra.reserve(uniq.size()); |
| size_t p = 0; |
| while (p < uniq.size()) { |
| int m = pool[uniq[p]].mU; |
| long long bestVal = -1; |
| while (p < uniq.size() && pool[uniq[p]].mU == m) { |
| int id = uniq[p++]; |
| long long v = pool[id].val; |
| if (v > bestVal) { |
| intra.push_back(id); |
| bestVal = v; |
| } |
| } |
| } |
| if (intra.empty()) return intra; |
|
|
| |
| vector<int> vols; |
| vols.reserve(intra.size()); |
| for (int id : intra) vols.push_back(pool[id].vU); |
| sort(vols.begin(), vols.end()); |
| vols.erase(unique(vols.begin(), vols.end()), vols.end()); |
| FenwickMax bit((int)vols.size()); |
|
|
| vector<int> kept; |
| kept.reserve(intra.size()); |
| p = 0; |
| while (p < intra.size()) { |
| int m = pool[intra[p]].mU; |
| size_t start = p; |
| while (p < intra.size() && pool[intra[p]].mU == m) p++; |
|
|
| |
| vector<int> groupKept; |
| groupKept.reserve(p - start); |
| for (size_t t = start; t < p; t++) { |
| int id = intra[t]; |
| int volU = pool[id].vU; |
| long long val = pool[id].val; |
| int pos = (int)(lower_bound(vols.begin(), vols.end(), volU) - vols.begin()) + 1; |
| if (bit.query(pos) >= val) continue; |
| groupKept.push_back(id); |
| kept.push_back(id); |
| } |
| |
| for (int id : groupKept) { |
| int volU = pool[id].vU; |
| long long val = pool[id].val; |
| int pos = (int)(lower_bound(vols.begin(), vols.end(), volU) - vols.begin()) + 1; |
| bit.update(pos, val); |
| } |
| } |
|
|
| |
| const int HARD_MAX = 150000; |
| const int BUCKET_MAX = 70000; |
| if ((int)kept.size() > HARD_MAX) { |
| int MB = 210, VB = 260; |
| int massStep = max(1, capMU / MB + 1); |
| int volStep = max(1, capVU / VB + 1); |
| vector<int> bestIdx(MB * VB, -1); |
| vector<long long> bestVal(MB * VB, -1); |
| for (int id : kept) { |
| const State& S = pool[id]; |
| int mb = min(MB - 1, S.mU / massStep); |
| int vb = min(VB - 1, S.vU / volStep); |
| int pos = mb * VB + vb; |
| if (S.val > bestVal[pos]) { |
| bestVal[pos] = S.val; |
| bestIdx[pos] = id; |
| } |
| } |
| vector<int> thinned; |
| thinned.reserve(MB * VB); |
| for (int id : bestIdx) if (id != -1) thinned.push_back(id); |
| if ((int)thinned.size() > BUCKET_MAX) { |
| sort(thinned.begin(), thinned.end(), [&](int a, int b) { return pool[a].val > pool[b].val; }); |
| thinned.resize(BUCKET_MAX); |
| } |
| return thinned; |
| } |
| return kept; |
| } |
|
|
| static vector<int> solveDPScaled(const vector<Item>& items, long long massUnit, long long volUnit, int orderMode) { |
| int n = (int)items.size(); |
| int capMU = (int)(CAP_M / massUnit); |
| int capVU = (int)(CAP_V / volUnit); |
|
|
| vector<Bundle> bundles; |
| bundles.reserve(200); |
|
|
| for (int i = 0; i < n; i++) { |
| int rem = items[i].q; |
| int p = 1; |
| while (rem > 0) { |
| int take = min(p, rem); |
| rem -= take; |
| p <<= 1; |
| long long mTot = (long long)take * items[i].m; |
| long long vTot = (long long)take * items[i].l; |
| int mU = (int)((mTot + massUnit - 1) / massUnit); |
| int vU = (int)((vTot + volUnit - 1) / volUnit); |
| if (mU <= capMU && vU <= capVU) { |
| Bundle b; |
| b.type = i; |
| b.cnt = take; |
| b.mU = mU; |
| b.vU = vU; |
| b.val = (long long)take * items[i].v; |
| bundles.push_back(b); |
| } |
| } |
| } |
|
|
| auto densityKey = [&](const Bundle& b) -> long double { |
| long double cost = (long double)b.mU / max(1, capMU) + (long double)b.vU / max(1, capVU); |
| if (cost <= 0) cost = 1e-18L; |
| return (long double)b.val / cost; |
| }; |
|
|
| if (orderMode == 1) { |
| sort(bundles.begin(), bundles.end(), [&](const Bundle& a, const Bundle& b) { |
| long double da = densityKey(a), db = densityKey(b); |
| if (da != db) return da > db; |
| return a.val > b.val; |
| }); |
| } else if (orderMode == 2) { |
| sort(bundles.begin(), bundles.end(), [&](const Bundle& a, const Bundle& b) { |
| if (a.val != b.val) return a.val > b.val; |
| if (a.mU != b.mU) return a.mU < b.mU; |
| return a.vU < b.vU; |
| }); |
| } |
|
|
| vector<State> pool; |
| pool.reserve(200000); |
| pool.push_back(State{0, 0, 0, -1, -1}); |
| vector<int> frontier; |
| frontier.reserve(1000); |
| frontier.push_back(0); |
|
|
| for (int bi = 0; bi < (int)bundles.size(); bi++) { |
| const Bundle& b = bundles[bi]; |
| vector<int> candidates; |
| candidates.reserve(frontier.size() * 2 + 4); |
| for (int id : frontier) candidates.push_back(id); |
|
|
| for (int id : frontier) { |
| const State& s = pool[id]; |
| int nm = s.mU + b.mU; |
| int nv = s.vU + b.vU; |
| if (nm > capMU || nv > capVU) continue; |
| State ns; |
| ns.mU = nm; |
| ns.vU = nv; |
| ns.val = s.val + b.val; |
| ns.prev = id; |
| ns.bundle = bi; |
| pool.push_back(ns); |
| candidates.push_back((int)pool.size() - 1); |
| } |
|
|
| frontier = pruneFrontier(pool, std::move(candidates), capMU, capVU); |
| if (frontier.empty()) frontier.push_back(0); |
| } |
|
|
| |
| int bestId = frontier[0]; |
| for (int id : frontier) if (pool[id].val > pool[bestId].val) bestId = id; |
|
|
| vector<int> cnt(n, 0); |
| int cur = bestId; |
| while (cur > 0) { |
| int bi = pool[cur].bundle; |
| if (bi >= 0) cnt[bundles[bi].type] += bundles[bi].cnt; |
| cur = pool[cur].prev; |
| if (cur < 0) break; |
| } |
| |
| for (int i = 0; i < n; i++) cnt[i] = min(cnt[i], items[i].q); |
| return cnt; |
| } |
|
|
| static void greedyFill(const vector<Item>& items, vector<int>& cnt) { |
| int n = (int)items.size(); |
| long long usedM = 0, usedV = 0; |
| for (int i = 0; i < n; i++) { |
| usedM += (long long)cnt[i] * items[i].m; |
| usedV += (long long)cnt[i] * items[i].l; |
| } |
|
|
| while (true) { |
| int best = -1; |
| long double bestScore = -1; |
| for (int i = 0; i < n; i++) { |
| if (cnt[i] >= items[i].q) continue; |
| if (usedM + items[i].m > CAP_M) continue; |
| if (usedV + items[i].l > CAP_V) continue; |
| long double cost = (long double)items[i].m / (long double)CAP_M + (long double)items[i].l / (long double)CAP_V; |
| if (cost <= 0) cost = 1e-18L; |
| long double score = (long double)items[i].v / cost; |
| if (score > bestScore) { |
| bestScore = score; |
| best = i; |
| } |
| } |
| if (best < 0) break; |
| long long rem = items[best].q - cnt[best]; |
| long long kM = (CAP_M - usedM) / items[best].m; |
| long long kV = (CAP_V - usedV) / items[best].l; |
| long long add = min<long long>(rem, min(kM, kV)); |
| if (add <= 0) break; |
| cnt[best] += (int)add; |
| usedM += add * items[best].m; |
| usedV += add * items[best].l; |
| } |
| } |
|
|
| static void localImprove(const vector<Item>& items, vector<int>& cnt) { |
| int n = (int)items.size(); |
| long long usedM = 0, usedV = 0; |
| long long usedVal = 0; |
| for (int i = 0; i < n; i++) { |
| usedM += (long long)cnt[i] * items[i].m; |
| usedV += (long long)cnt[i] * items[i].l; |
| usedVal += (long long)cnt[i] * items[i].v; |
| } |
|
|
| auto applyGreedy = [&]() { |
| greedyFill(items, cnt); |
| usedM = usedV = usedVal = 0; |
| for (int i = 0; i < n; i++) { |
| usedM += (long long)cnt[i] * items[i].m; |
| usedV += (long long)cnt[i] * items[i].l; |
| usedVal += (long long)cnt[i] * items[i].v; |
| } |
| }; |
|
|
| applyGreedy(); |
|
|
| auto candidateRemovals = [&](int c) { |
| vector<int> r; |
| int lim = min(c, 20); |
| for (int x = 1; x <= lim; x++) r.push_back(x); |
| for (int x = 1; x < c; x <<= 1) r.push_back(min(c, x)); |
| r.push_back(c); |
| sort(r.begin(), r.end()); |
| r.erase(unique(r.begin(), r.end()), r.end()); |
| return r; |
| }; |
|
|
| for (int iter = 0; iter < 120; iter++) { |
| long long bestDelta = 0; |
| int bestI = -1, bestJ = -1, bestR = 0, bestA = 0; |
|
|
| for (int i = 0; i < n; i++) { |
| if (cnt[i] <= 0) continue; |
| vector<int> rs = candidateRemovals(cnt[i]); |
| for (int r : rs) { |
| long long newUsedM = usedM - (long long)r * items[i].m; |
| long long newUsedV = usedV - (long long)r * items[i].l; |
| if (newUsedM < 0 || newUsedV < 0) continue; |
|
|
| long long availM = CAP_M - newUsedM; |
| long long availV = CAP_V - newUsedV; |
|
|
| for (int j = 0; j < n; j++) { |
| long long remJ = items[j].q - cnt[j]; |
| if (j == i) remJ += r; |
| if (remJ <= 0) continue; |
| if (items[j].m > availM || items[j].l > availV) continue; |
| long long maxA = min(remJ, min(availM / items[j].m, availV / items[j].l)); |
| if (maxA <= 0) continue; |
|
|
| long long delta = maxA * items[j].v - (long long)r * items[i].v; |
| if (delta > bestDelta) { |
| bestDelta = delta; |
| bestI = i; bestJ = j; |
| bestR = r; |
| bestA = (int)maxA; |
| } |
| } |
| } |
| } |
|
|
| if (bestDelta <= 0) break; |
|
|
| |
| cnt[bestI] -= bestR; |
| cnt[bestJ] += bestA; |
| |
| for (int i = 0; i < n; i++) cnt[i] = max(0, min(cnt[i], items[i].q)); |
|
|
| usedM = usedV = usedVal = 0; |
| for (int i = 0; i < n; i++) { |
| usedM += (long long)cnt[i] * items[i].m; |
| usedV += (long long)cnt[i] * items[i].l; |
| usedVal += (long long)cnt[i] * items[i].v; |
| } |
| if (usedM > CAP_M || usedV > CAP_V) { |
| |
| for (int i = 0; i < n; i++) cnt[i] = 0; |
| usedM = usedV = usedVal = 0; |
| } |
| applyGreedy(); |
| } |
| } |
|
|
| static vector<int> greedyLambda(const vector<Item>& items, long double lambda) { |
| int n = (int)items.size(); |
| vector<int> cnt(n, 0); |
| vector<int> ord(n); |
| iota(ord.begin(), ord.end(), 0); |
| auto dens = [&](int i) -> long double { |
| long double cost = (long double)items[i].m / (long double)CAP_M + lambda * (long double)items[i].l / (long double)CAP_V; |
| if (cost <= 0) cost = 1e-18L; |
| return (long double)items[i].v / cost; |
| }; |
| sort(ord.begin(), ord.end(), [&](int a, int b) { |
| long double da = dens(a), db = dens(b); |
| if (da != db) return da > db; |
| return items[a].v > items[b].v; |
| }); |
|
|
| long long usedM = 0, usedV = 0; |
| for (int i : ord) { |
| if (items[i].m > CAP_M || items[i].l > CAP_V) continue; |
| long long k = min<long long>(items[i].q, |
| min((CAP_M - usedM) / items[i].m, (CAP_V - usedV) / items[i].l)); |
| if (k <= 0) continue; |
| cnt[i] = (int)k; |
| usedM += k * items[i].m; |
| usedV += k * items[i].l; |
| } |
| greedyFill(items, cnt); |
| return cnt; |
| } |
|
|
| int main() { |
| ios::sync_with_stdio(false); |
| cin.tie(nullptr); |
|
|
| string input((istreambuf_iterator<char>(cin)), istreambuf_iterator<char>()); |
| vector<Item> items; |
| if (!parseInputJSON(input, items)) { |
| |
| cout << "{}\n"; |
| return 0; |
| } |
|
|
| int n = (int)items.size(); |
| vector<Solution> candidates; |
|
|
| |
| vector<pair<long long,long long>> scales = {{1000,1000}, {2000,2000}, {500,500}, {5000,5000}}; |
| for (auto [mu, vu] : scales) { |
| if (CAP_M % mu != 0 || CAP_V % vu != 0) continue; |
| for (int orderMode = 0; orderMode <= 2; orderMode++) { |
| vector<int> c = solveDPScaled(items, mu, vu, orderMode); |
| Solution sol; |
| sol.cnt = std::move(c); |
| sol.value = evalSolution(items, sol.cnt, sol.mass, sol.vol); |
| if (sol.mass <= CAP_M && sol.vol <= CAP_V) candidates.push_back(sol); |
| } |
| } |
|
|
| |
| vector<long double> lambdas = {0.0L, 0.15L, 0.3L, 0.6L, 1.0L, 1.7L, 3.0L, 6.0L}; |
| for (auto lam : lambdas) { |
| vector<int> c = greedyLambda(items, lam); |
| Solution sol; |
| sol.cnt = std::move(c); |
| sol.value = evalSolution(items, sol.cnt, sol.mass, sol.vol); |
| if (sol.mass <= CAP_M && sol.vol <= CAP_V) candidates.push_back(sol); |
| } |
|
|
| |
| sort(candidates.begin(), candidates.end(), [&](const Solution& a, const Solution& b) { |
| return a.value > b.value; |
| }); |
|
|
| int improveCount = min<int>((int)candidates.size(), 5); |
| Solution best; |
| best.value = -1; |
| best.cnt.assign(n, 0); |
|
|
| for (int idx = 0; idx < (int)candidates.size(); idx++) { |
| Solution sol = candidates[idx]; |
| if (idx < improveCount) { |
| localImprove(items, sol.cnt); |
| sol.value = evalSolution(items, sol.cnt, sol.mass, sol.vol); |
| } |
| if (sol.mass <= CAP_M && sol.vol <= CAP_V && sol.value > best.value) { |
| best = std::move(sol); |
| } |
| } |
|
|
| |
| for (int i = 0; i < n; i++) best.cnt[i] = max(0, min(best.cnt[i], items[i].q)); |
| long long mCheck = 0, vCheck = 0; |
| long long valCheck = evalSolution(items, best.cnt, mCheck, vCheck); |
| if (mCheck > CAP_M || vCheck > CAP_V) { |
| |
| best.cnt = greedyLambda(items, 1.0L); |
| for (int i = 0; i < n; i++) best.cnt[i] = max(0, min(best.cnt[i], items[i].q)); |
| } |
|
|
| |
| cout << "{"; |
| for (int i = 0; i < n; i++) { |
| if (i) cout << ","; |
| cout << "\n" << " \"" << items[i].name << "\": " << best.cnt[i]; |
| } |
| if (n) cout << "\n"; |
| cout << "}\n"; |
| return 0; |
| } |