1 条题解
-
0
题目回顾
A. Furniture Store
每次测试时间限制: 秒
每次测试内存限制: 兆字节一家家具制造销售公司的网站上有 种不同型号的沙发,编号从 到 。每款沙发有唯一的价格;第 款沙发的价格为 。
每位访问网站的顾客有自己的预算 (愿意为沙发支付的金额)。顾客浏览沙发列表时,会寻找第一个价格不超过 的沙发并订购。如果没有这样的沙发,顾客将离开网站,不购买任何东西。
公司希望找出永远不会被订购的沙发型号。你的任务是:确定不存在任何预算 能让顾客选中该型号沙发的那些型号。
输入格式
第一行包含一个整数 — 测试用例的数量。
每个测试用例的第一行包含一个整数 。
第二行包含 个互不相同的整数 。输出格式
对于每个测试用例:
第一行输出一个整数 — 无法被订购的型号数量。
第二行按升序输出 个整数 — 无法被订购的型号编号。
题解
关键观察
顾客的购买规则是:按照编号从小到大浏览,遇到第一个价格不超过预算 的沙发就下单。
注意到,如果存在某个 且 ,那么:
- 当某位顾客的预算 足以购买第 款沙发时(即 ),必然也有 ;
- 由于 排在 前面,顾客会优先选择第 款沙发,从而第 款永远不会被购买。
反之,若 严格小于它之前的所有价格:
则当顾客的预算 满足:
时,前面所有沙发都超出了预算,唯有第 款沙发可以被购买。因此,该款沙发是会被购买的。
充分必要条件
综上,我们得到核心结论:
$$\boxed{ \text{第 } i \text{ 款沙发会被订购} \iff a_i = \min\{a_1, a_2, \dots, a_i\} } $$换句话说,永远不会被订购的沙发就是那些在其前缀中不是最小值的型号。
解法
我们只需遍历一次数组 ,维护当前前缀的最小值
min_so_far:- 初始令
- 对于每个 从 到 :
- 若 :说明 是其前缀的最小值,会被订购,并更新 ;
- 否则: 不是前缀最小值,不会被订购,将 加入答案列表。
由于按 从小到大遍历,加入答案列表的索引自然满足升序要求。
最终算法
- 初始化答案列表
ans为空。 - 初始化
min_so_far = ∞。 - 对于 :
- 如果 :
- 否则:
- 将 加入
ans
- 将 加入
- 如果 :
- 输出
ans的大小和内容。
复杂度分析
每个测试用例只需 时间遍历一次数组,,总用例数 ,完全可行。空间复杂度 用于存储输入数组和答案。
代码实现(C++)
#include <bits/stdc++.h> using namespace std; void solve() { int n; cin >> n; vector<int> a(n); for (int i = 0; i < n; i++) { cin >> a[i]; } vector<int> ans; int min_so_far = INT_MAX; for (int i = 0; i < n; i++) { if (a[i] < min_so_far) { min_so_far = a[i]; // 是前缀最小值,会被购买 } else { ans.push_back(i + 1); // 不是前缀最小值,不会被购买 } } cout << ans.size() << "\n"; for (int i = 0; i < ans.size(); i++) { cout << ans[i] << " \n"[i == ans.size() - 1]; } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
示例验证
输入:
4 3 1 2 3 4 4 6 2 1 1 100 6 7 5 8 4 6 2处理过程:
-
用例 1:
前缀最小值变化:
是最小值(卖); 不是(不卖); 不是(不卖)
→ 答案:,数量 ✅ -
用例 2:
前缀最小值变化:
卖; 不卖; 卖(新最小值); 卖(新最小值)
→ 答案:,数量 ✅ -
用例 3:
前缀最小值:
卖
→ 答案为空,数量 ✅ -
用例 4:
前缀最小值变化:
卖; 卖; 不卖; 卖; 不卖; 卖
→ 答案:,数量 ✅
输出:
2 2 3 1 2 0 2 3 5与题目示例完全一致。
- 1
信息
- ID
- 6650
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 2
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者