[NOI Online #1 入门组] 文具订购

GitHub跳转原题关系图返回列表

先尽量买完整套餐,再在很小的剩余金额里枚举额外物品数。

OJ: luogu

题目 ID: P6188

难度:普及/提高-

标签:数学枚举

日期: 2026-06-18 21:30

题意

n 元班费购买圆规、笔和笔记本,价格分别是 7,4,3 元。 要求正好花完钱,并且按优先级依次最大化:

  1. min(a,b,c)
  2. a+b+c

输出最优方案;若无解输出 -1

思路

先看一个可以直接验证想法的朴素解:

直接枚举所有满足 7a+4b+3c=n 的三元组,再按题目优先级更新答案。

cpp
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin >> n;

    int best_min = -1;
    int best_cnt = -1;
    int ans_a = -1, ans_b = -1, ans_c = -1;

    for (int a = 0; 7 * a <= n; a++) {
        for (int b = 0; 7 * a + 4 * b <= n; b++) {
            int left = n - 7 * a - 4 * b;
            if (left % 3) continue;
            int c = left / 3;
            int mn = min(a, min(b, c));
            int cnt = a + b + c;
            if (mn > best_min || (mn == best_min && cnt > best_cnt)) {
                best_min = mn;
                best_cnt = cnt;
                ans_a = a;
                ans_b = b;
                ans_c = c;
            }
        }
    }

    if (best_min == -1) {
        cout << -1 << '\n';
    } else {
        cout << ans_a << ' ' << ans_b << ' ' << ans_c << '\n';
    }
    return 0;
}

关键在于把答案拆成“完整套餐 + 额外购买”。

如果先买 t(1,1,1),就会花掉 14t 元。 而题目第 2 条本质上就是要求这个 t 尽量大。

于是只要先取最大的可行 t,再在剩余金额里让额外件数尽量多即可。 这时剩余金额的范围很小,直接枚举补充方案就够了。

代码

cpp
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin >> n;

    int t = n / 14;
    int rem = n % 14;
    if (rem == 1 || rem == 2 || rem == 5) {
        t--;
        rem += 14;
    }

    if (t < 0) {
        cout << -1 << '\n';
        return 0;
    }

    int best_a = -1, best_b = -1, best_c = -1;
    int best_cnt = -1;

    for (int a = 0; 7 * a <= rem; a++) {
        for (int b = 0; 7 * a + 4 * b <= rem; b++) {
            int left = rem - 7 * a - 4 * b;
            if (left % 3) continue;
            int c = left / 3;
            int cnt = a + b + c;
            if (cnt > best_cnt) {
                best_cnt = cnt;
                best_a = a;
                best_b = b;
                best_c = c;
            }
        }
    }

    if (best_cnt == -1) {
        cout << -1 << '\n';
        return 0;
    }

    cout << t + best_a << ' ' << t + best_b << ' ' << t + best_c << '\n';
    return 0;
}

复杂度

主解只在常数范围内枚举,时间复杂度 O(1)O(1),空间复杂度 O(1)O(1)

总结

这题的核心是先按“成套数”拆分,再在小剩余里补最优方案。