Treasure Packing
Instruction
<strong>Important:</strong> Each item of the same type has the same mass and volume. For example, all goblets have the same mass and volume.
<h2>Your Task</h2> <p>Write a program to help the hero determine what items they should put in the bag to <strong>maximize value</strong> while accounting for the constraints on the bag.</p> <h3>Constraints</h3> <ul> <li><strong>Weight limit:</strong> 20 kg (20,000,000 mg)</li> <li><strong>Volume limit:</strong> 25 liters (25,000,000 µliters)</li> </ul> <h2>Input Format</h2> <p>The input is <strong>JSON</strong> keyed by treasure category name (e.g., <code>"goblet"</code>).</p> <ul> <li>Every category has a <strong>unique name</strong> composed of at most 100 lowercase ASCII characters</li> <li>There are <strong>exactly twelve</strong> treasure categories</li> </ul> <p>Each corresponding value is a list with:</p> <p>1. <strong>q</strong> (1 �?q �?10,000): Maximum number of treasures of this category that can be looted 2. <strong>v</strong> (0 < v �?10�?: Value of one item 3. <strong>m</strong> (0 < m �?20×10�?: Mass in <strong>mg</strong> of one item (1 million mg per kg) 4. <strong>l</strong> (0 < l �?25×10�?: Volume in <strong>µliters</strong> of one item (1 million µliters per liter)</p> <h3>Sample Input</h3> <pre><code class="language-json">{ "circlet": [19, 113005, 146800, 514247], "coppercoin": [887, 6171, 12593, 18081], "crown": [13, 726439, 1079353, 1212213], "dagger": [21, 279513, 558367, 522344], "figurine": [18, 26272, 1488281, 2295986], "goblet": [22, 300053, 698590, 986387], "goldcoin": [129, 14426, 82176, 27597], "helm": [3, 974983, 2470209, 882803], "jewelry": [23, 272450, 171396, 262226], "plate": [22, 98881, 246257, 363420], "silvercoin": [288, 12587, 53480, 28654], "torc": [17, 244957, 388222, 500000] }</code></pre> <h2>Output Format</h2> <p>Print a <strong>JSON</strong> with the same keys as the input, but with <strong>single nonnegative integer values</strong> giving the numbers of items of that category added to the bag in your solution.</p> <h3>Sample Output</h3> <pre><code class="language-json">{ "circlet": 2, "coppercoin": 8, "crown": 13, "dagger": 0, "figurine": 0, "goblet": 0, "goldcoin": 0, "helm": 0, "jewelry": 23, "plate": 0, "silvercoin": 1, "torc": 4 }</code></pre> <h2>Scoring</h2> <p>Producing an algorithm that always generates optimal solutions is very difficult, so your solution only needs to be <strong>"good enough"</strong>.</p> <p>We will compare your output to: 1. A <strong>baseline heuristic</strong> provided by the NSA 2. A <strong>best effort</strong> to compute the true optimum</p> <h3>Score Calculation</h3> <p>Your score on this problem is the <strong>average of the per-test-case score</strong>:</p> <pre><code class="language-">Score = 100 × clamp((your_value - baseline_value) / (best_value - baseline_value), 0, 1)</code></pre>Where:
<ul> <li><strong>your_value</strong>: Total value of treasures in your solution</li> <li><strong>baseline_value</strong>: Total value from the NSA heuristic</li> <li><strong>best_value</strong>: Best known or computed optimal value</li> </ul><strong>Requirements:</strong>
<ul> <li>You <strong>must beat the NSA heuristic</strong> to receive points</li> <li>The better your solution compared to the optimum, the higher your score</li> </ul> <h2>Problem Type</h2> <p>This is a <strong>variant of the 0/1 Knapsack Problem</strong> with two constraints (weight and volume) instead of one. This is also known as the <strong>Multidimensional Knapsack Problem</strong>.</p> <h3>Key Differences from Classic Knapsack:</h3> <ul> <li><strong>Dual constraints:</strong> Must satisfy both weight AND volume limits</li> <li><strong>Multiple copies:</strong> Can take multiple items of the same type (up to q items)</li> <li><strong>Approximate solutions:</strong> Getting close to optimal is sufficient for a good score</li> </ul> <hr> <h2>Problem Metadata</h2> <strong>Time Limit:</strong> 1 second <strong>Memory Limit:</strong> 1024 MB <hr> <h2>Model Information</h2> <p><strong>Model:</strong> GPT 5 Thinking</p> <p><strong>Score:</strong> 94.0</p> <h3>Solution Code</h3> <pre><code>#include <bits/stdc++.h> using namespace std;struct Item { string name; long long q, v, m, l; };
struct Solution { vector<long long> x; long long totalValue = 0; long long totalM = 0; long long totalL = 0; };
struct Parser { string s; size_t pos = 0;
Parser(const string& str) : s(str), pos(0) {}
void skip() {
while (pos < s.size() && (s[pos] == ' ' || s[pos] == '\n' || s[pos] == '\t' || s[pos] == '\r')) pos++;
}
bool expect(char c) {
skip();
if (pos < s.size() && s[pos] == c) { pos++; return true; }
return false;
}
string parseString() {
skip();
if (pos >= s.size() || s[pos] != '"') return "";
pos++;
string res;
while (pos < s.size() && s[pos] != '"') {
// no escape handling needed for given constraints (lowercase ascii)
res.push_back(s[pos++]);
}
if (pos < s.size() && s[pos] == '"') pos++;
return res;
}
long long parseInt() {
skip();
bool neg = false;
if (pos < s.size() && s[pos] == '-') { neg = true; pos++; }
long long val = 0;
while (pos < s.size() && s[pos] >= '0' && s[pos] <= '9') {
val = val * 10 + (s[pos] - '0');
pos++;
}
return neg ? -val : val;
}
vector<Item> parseItems() {
vector<Item> items;
skip();
if (!expect('{')) return items;
while (true) {
skip();
if (pos < s.size() && s[pos] == '}') { pos++; break; }
string key = parseString();
skip();
expect(':');
skip();
expect('[');
long long q = parseInt();
skip(); expect(',');
long long v = parseInt();
skip(); expect(',');
long long m = parseInt();
skip(); expect(',');
long long l = parseInt();
skip(); expect(']');
items.push_back({key, q, v, m, l});
skip();
if (pos < s.size() && s[pos] == ',') { pos++; continue; }
skip();
if (pos < s.size() && s[pos] == '}') { pos++; break; }
}
return items;
}
};
static inline long long ceil_div(long long a, long long b) { if (b <= 0) return LLONG_MAX/4; if (a <= 0) return 0; return (a + b - 1) / b; }
struct Metric { long double lam; long double mu; int mode; // 0 = linear combo; 1 = max normalized };
int main() { ios::sync_with_stdio(false); cin.tie(nullptr);
// Read entire input
string input, line;
stringstream buffer;
while (getline(cin, line)) {
buffer << line << "\n";
}
input = buffer.str();
Parser parser(input);
vector<Item> items = parser.parseItems();
int n = (int)items.size();
if (n == 0) {
// Fallback: print empty JSON
cout << "{\n";
for (int i = 0; i < n; i++) {
cout << " \"" << items[i].name << "\": 0";
if (i + 1 < n) cout << ",\n"; else cout << "\n";
}
cout << "}\n";
return 0;
}
const long long Mcap = 20LL * 1000 * 1000;
const long long Lcap = 25LL * 1000 * 1000;
auto now = [](){ return chrono::steady_clock::now(); };
auto start = now();
auto timePassedMs = [&](double ms){
return chrono::duration<double, milli>(now() - start).count() >= ms;
};
// Helper to compute solution totals
auto computeTotals = [&](Solution& sol) {
sol.totalValue = 0;
sol.totalM = 0;
sol.totalL = 0;
for (int i = 0; i < n; i++) {
if (sol.x[i] < 0) sol.x[i] = 0;
if (sol.x[i] > items[i].q) sol.x[i] = items[i].q;
sol.totalValue += sol.x[i] * items[i].v;
sol.totalM += sol.x[i] * items[i].m;
sol.totalL += sol.x[i] * items[i].l;
}
};
// Feasibility check
auto isFeasible = [&](const Solution& sol)->bool{
if (sol.totalM > Mcap || sol.totalL > Lcap) return false;
for (int i = 0; i < n; i++) if (sol.x[i] < 0 || sol.x[i] > items[i].q) return false;
return true;
};
// Greedy by metric
auto greedyByMetric = [&](const Metric& metric)->Solution {
vector<int> order(n);
iota(order.begin(), order.end(), 0);
vector<long double> dens(n, 0.0L);
for (int i = 0; i < n; i++) {
long double nm = (long double)items[i].m / (long double)Mcap;
long double nl = (long double)items[i].l / (long double)Lcap;
long double denom;
if (metric.mode == 1) {
denom = max(nm, nl);
} else {
denom = metric.lam * nm + metric.mu * nl;
}
if (denom <= 0) {
dens[i] = 1e300L; // extremely high density
} else {
dens[i] = (long double)items[i].v / denom;
}
}
stable_sort(order.begin(), order.end(), [&](int a, int b){
if (dens[a] == dens[b]) return items[a].v > items[b].v;
return dens[a] > dens[b];
});
Solution sol;
sol.x.assign(n, 0);
long long remM = Mcap, remL = Lcap;
for (int idx : order) {
if (items[idx].m > remM || items[idx].l > remL) continue;
long long kM = items[idx].m > 0 ? remM / items[idx].m : (long long)4e18;
long long kL = items[idx].l > 0 ? remL / items[idx].l : (long long)4e18;
long long k = min({ (long long)items[idx].q, kM, kL });
if (k <= 0) continue;
sol.x[idx] += k;
remM -= k * items[idx].m;
remL -= k * items[idx].l;
// Early break if bag is "full" for both dimensions (cannot add any item)
}
computeTotals(sol);
return sol;
};
// Single item greedy (only one type)
auto singleTypeGreedy = [&](int idx)->Solution {
Solution sol;
sol.x.assign(n, 0);
long long kM = items[idx].m > 0 ? Mcap / items[idx].m : (long long)4e18;
long long kL = items[idx].l > 0 ? Lcap / items[idx].l : (long long)4e18;
long long k = min({ (long long)items[idx].q, kM, kL });
if (k > 0) {
sol.x[idx] = k;
}
computeTotals(sol);
return sol;
};
// Try to add one item j by possibly removing others greedily; returns true if improved
auto tryAddImprove = [&](Solution& sol, int j)->bool {
if (sol.x[j] >= items[j].q) return false;
long long remM = Mcap - sol.totalM;
long long remL = Lcap - sol.totalL;
if (items[j].m <= remM && items[j].l <= remL) {
// Always beneficial since v > 0
sol.x[j] += 1;
sol.totalM += items[j].m;
sol.totalL += items[j].l;
sol.totalValue += items[j].v;
return true;
}
// Need to remove items to fit j
long long needM0 = max(0LL, items[j].m - remM);
long long needL0 = max(0LL, items[j].l - remL);
if (needM0 == 0 && needL0 == 0) {
// Shouldn't get here due to first check
return false;
}
auto attemptRemoval = [&](double mode)->bool {
// mode 0: weights proportional to deficits; mode 1: binary 0/1
long double a, b;
if (mode < 0.5) {
a = (long double)needM0 / (long double)Mcap;
b = (long double)needL0 / (long double)Lcap;
} else {
a = needM0 > 0 ? 1.0L : 0.0L;
b = needL0 > 0 ? 1.0L : 0.0L;
}
vector<long long> remCnt(n, 0);
long long removedVal = 0;
long long curRemM = remM;
long long curRemL = remL;
long long needM = needM0;
long long needL = needL0;
int guard = 0;
while ((needM > 0 || needL > 0) && removedVal < items[j].v) {
int best = -1;
long double bestScore = 1e300L;
for (int i = 0; i < n; i++) {
if (i == j) continue;
long long avail = sol.x[i] - remCnt[i];
if (avail <= 0) continue;
if (needM > 0 || needL > 0) {
// Only consider those freeing at least some needed resource
bool useful = false;
if (needM > 0 && items[i].m > 0) useful = true;
if (needL > 0 && items[i].l > 0) useful = true;
if (!useful) continue;
}
long double denom = a * (long double)items[i].m + b * (long double)items[i].l;
if (denom <= 0) continue;
long double s = (long double)items[i].v / denom;
// Respect value bound: must allow removing at least 1 while still potentially improving
long long maxRemByVal = (items[j].v - 1 - removedVal) / items[i].v;
if (maxRemByVal <= 0) continue;
if (s < bestScore) { bestScore = s; best = i; }
}
if (best == -1) {
// No removable items without losing more value than gain or not useful
break;
}
// Remove in batch
long long maxRemByVal = (items[j].v - 1 - removedVal) / items[best].v;
long long avail = sol.x[best] - remCnt[best];
if (avail <= 0 || maxRemByVal <= 0) break;
long long kNeedM = needM > 0 ? ceil_div(needM, items[best].m) : 0;
long long kNeedL = needL > 0 ? ceil_div(needL, items[best].l) : 0;
long long kNeed = max(kNeedM, kNeedL);
if (kNeed <= 0) kNeed = 1;
long long k = min({avail, maxRemByVal, kNeed});
if (k <= 0) k = 1;
// Apply removal
remCnt[best] += k;
removedVal += k * items[best].v;
curRemM += k * items[best].m;
curRemL += k * items[best].l;
needM = max(0LL, items[j].m - curRemM);
needL = max(0LL, items[j].l - curRemL);
guard++;
if (guard > 100000) break; // safety
}
if (needM > 0 || needL > 0) return false;
if (removedVal >= items[j].v) return false;
// Commit changes
for (int i = 0; i < n; i++) {
if (remCnt[i] > 0) {
sol.x[i] -= remCnt[i];
sol.totalM -= remCnt[i] * items[i].m;
sol.totalL -= remCnt[i] * items[i].l;
sol.totalValue -= remCnt[i] * items[i].v;
}
}
// Now add j
sol.x[j] += 1;
sol.totalM += items[j].m;
sol.totalL += items[j].l;
sol.totalValue += items[j].v;
return true;
};
if (attemptRemoval(0.0)) return true;
if (attemptRemoval(1.0)) return true;
return false;
};
auto localImprove = [&](Solution& sol) {
// Try several passes, attempt to add items with removal
// Order by descending value density using equal weights
vector<int> order(n);
iota(order.begin(), order.end(), 0);
vector<long double> density(n, 0.0L);
for (int i = 0; i < n; i++) {
long double nm = (long double)items[i].m / (long double)Mcap;
long double nl = (long double)items[i].l / (long double)Lcap;
long double denom = nm + nl;
if (denom <= 0) density[i] = 1e300L;
else density[i] = (long double)items[i].v / denom;
}
stable_sort(order.begin(), order.end(), [&](int a, int b){
if (density[a] == density[b]) return items[a].v > items[b].v;
return density[a] > density[b];
});
bool improved = true;
int passes = 0;
while (improved && passes < 4 && !timePassedMs(900.0)) {
improved = false;
for (int idx = 0; idx < n && !timePassedMs(930.0); idx++) {
int j = order[idx];
// Try to add as many as possible with improvement
int innerGuard = 0;
while (!timePassedMs(950.0)) {
bool ok = tryAddImprove(sol, j);
if (!ok) break;
improved = true;
innerGuard++;
if (innerGuard > 10000) break; // safety
}
}
passes++;
}
};
// Generate metrics
vector<Metric> metrics;
vector<long double> ws = {0.0L, 0.1L, 0.25L, 0.5L, 1.0L, 2.0L, 4.0L};
for (auto lam : ws) {
for (auto mu : ws) {
if (lam == 0.0L && mu == 0.0L) continue;
metrics.push_back({lam, mu, 0});
}
}
// Special max-normalized metric
metrics.push_back({1.0L, 1.0L, 1});
// Additional skewed metrics
metrics.push_back({1.0L, 0.0L, 0});
metrics.push_back({0.0L, 1.0L, 0});
metrics.push_back({1.0L, 3.0L, 0});
metrics.push_back({3.0L, 1.0L, 0});
metrics.push_back({1.0L, 5.0L, 0});
metrics.push_back({5.0L, 1.0L, 0});
// Random metrics
mt19937_64 rng((uint64_t)chrono::high_resolution_clock::now().time_since_epoch().count());
uniform_real_distribution<long double> dist(0.05L, 5.0L);
for (int r = 0; r < 16; r++) {
long double lam = dist(rng);
long double mu = dist(rng);
metrics.push_back({lam, mu, 0});
}
// Evaluate candidates
Solution best;
best.x.assign(n, 0);
computeTotals(best); // zero solution
// Single-type baseline candidates
for (int i = 0; i < n; i++) {
Solution s = singleTypeGreedy(i);
if (isFeasible(s) && s.totalValue > best.totalValue) best = s;
if (timePassedMs(100.0)) ; // no-op, just force possible branch prediction
}
// Metrics-based greedy + improvement
for (size_t mi = 0; mi < metrics.size(); mi++) {
if (timePassedMs(980.0)) break;
Solution s = greedyByMetric(metrics[mi]);
if (isFeasible(s)) {
if (!timePassedMs(960.0)) localImprove(s);
if (s.totalValue > best.totalValue) best = s;
}
}
// Final safety feasibility
if (!isFeasible(best)) {
// Attempt to repair by dropping items with worst value density until feasible
// Compute density with equal weights
vector<pair<long double,int>> dens;
for (int i = 0; i < n; i++) {
long double nm = (long double)items[i].m / (long double)Mcap;
long double nl = (long double)items[i].l / (long double)Lcap;
long double denom = nm + nl;
long double d = denom > 0 ? (long double)items[i].v / denom : 1e300L;
dens.push_back({d, i});
}
sort(dens.begin(), dens.end(), [](auto& A, auto& B){ return A.first < B.first; }); // remove worst first
for (auto &p : dens) {
int i = p.second;
while ((best.totalM > Mcap || best.totalL > Lcap) && best.x[i] > 0) {
best.x[i]--;
best.totalM -= items[i].m;
best.totalL -= items[i].l;
best.totalValue -= items[i].v;
}
if (best.totalM <= Mcap && best.totalL <= Lcap) break;
}
if (!isFeasible(best)) {
// As last resort, empty solution
best.x.assign(n, 0);
computeTotals(best);
}
}
// Output JSON with same keys and order as input
cout << "{\n";
for (int i = 0; i < n; i++) {
cout << " \"" << items[i].name << "\": " << best.x[i];
if (i + 1 < n) cout << ",\n"; else cout << "\n";
}
cout << "}\n";
return 0;
}</code></pre>
Created by
Website template modified from https://www.tbench.ai/.