C - 商品検索システム / Product Search System 解説 by admin
GPT 5.2 High概要
各クエリ価格 \(X\) について、同価格の商品があれば商品番号順に全ての名前を出力し、なければ「最も近い価格」の商品名を 1 つ提案する問題です。
考察
重要なのは次の 2 点です。
「価格がちょうど一致」する場合
同じ価格の商品をすべて、商品番号の小さい順に出力します。入力は商品番号 \(1,2,\dots,N\) の順に与えられるので、同じ価格ごとに名前を配列に追加していけば、その配列は自然に商品番号順になります。一致がない場合は「最も近い価格」
素朴に各クエリごとに全商品を走査して最小の \(|V_i-X|\) を探すと、\(O(NQ)\) で最大 \(10^{10}\) となり TLE します。
そこで「価格」のみに注目し、存在する価格の集合をソートしておけば、二分探索で \(X\) の近く(直前と直後)の価格だけを見れば十分です。
最も近い候補は、- \(X\) より小さい側で最大の価格(直前)
- \(X\) 以上で最小の価格(直後)
のどちらかになります。
さらに、差が同じ(例:\(X=100\)、価格が 98 と 102)なら 商品番号が最小のものを選ぶ必要があります。ここで「ある価格に属する商品のうち最小の商品番号」を前計算しておけば、価格同士の比較だけでタイブレークまで処理できます。
アルゴリズム
前処理として次を作ります。
price_to_names[v]:価格 \(v\) の商品の名前を、商品番号順に並べたリスト
(入力順にappendするだけで商品番号順が保たれる)price_to_min[v] = (min_index, name):価格 \(v\) を持つ商品のうち、商品番号が最小の商品の(番号, 名前)prices:登場する価格(price_to_minのキー)を昇順ソートした配列
各クエリ \(X\) は以下で処理します。
- もし
price_to_names[X]が存在するなら、それをスペース区切りで出力。 - 存在しないなら、
pricesに対して二分探索(bisect_left)で挿入位置posを求める。pos==0:全ての価格が \(X\) より大 → 最小価格が最も近いpos==len(prices):全ての価格が \(X\) より小 → 最大価格が最も近い- それ以外:直前の価格
lo_p=prices[pos-1]と直後の価格hi_p=prices[pos]を比べる
差 \(d_\mathrm{lo}=X-lo_p\), \(d_\mathrm{hi}=hi_p-X\) を比較し、- 小さい方の価格に属する
price_to_min[...]の名前を出力 - 差が同じなら、
price_to_min[lo_p]とprice_to_min[hi_p]の 商品番号が小さい方の名前を出力
- 小さい方の価格に属する
具体例
価格一覧が [80, 120, 200] のとき、\(X=150\) なら直前は 120、直後は 200。差は 30 と 50 なので 120 の商品の(最小番号の)名前を提案します。
\(X=160\) なら差が 40 と 40 で同じなので、120 側の最小番号商品と 200 側の最小番号商品を比べて、番号が小さい方を出します。
計算量
- 時間計算量:
前処理 \(O(N)\)、価格ソート \(O(M\log M)\)(\(M\) は異なる価格数、\(M\le N\))、各クエリ二分探索 \(O(\log M)\) より全体で \(O(N + M\log M + Q\log M)\)
※一致価格の出力は名前列を結合して出すため、追加で「出力される文字数」に比例します(問題文より総量は \(10^6\) 以下)。 - 空間計算量: \(O(N + M)\)(価格ごとの名前リストと、価格配列など)
実装のポイント
同価格商品の出力順は「商品番号順」なので、入力順にリストへ
appendするだけでよい。「最も近い価格が複数」になったときのために、各価格について 最小の商品番号の商品を
price_to_minに持っておく(これがないと同価格内で追加探索が必要になる)。二分探索では「直前・直後」の 2 価格だけを見れば十分。
\(N,Q\) が大きいので、Python では
sys.stdin.buffer.read()による高速入力と、出力を配列に貯めて最後にまとめて出すのが安全です。ソースコード
import sys
from bisect import bisect_left
def main():
data = sys.stdin.buffer.read().split()
it = iter(data)
N = int(next(it))
Q = int(next(it))
price_to_names = {}
price_to_min = {} # price -> (min_index, name)
for i in range(1, N + 1):
s = next(it).decode()
v = int(next(it))
lst = price_to_names.get(v)
if lst is None:
price_to_names[v] = [s]
else:
lst.append(s)
cur = price_to_min.get(v)
if cur is None or i < cur[0]:
price_to_min[v] = (i, s)
prices = sorted(price_to_min.keys())
out_lines = []
for _ in range(Q):
x = int(next(it))
names = price_to_names.get(x)
if names is not None:
out_lines.append(" ".join(names))
continue
pos = bisect_left(prices, x)
if pos == 0:
out_lines.append(price_to_min[prices[0]][1])
elif pos == len(prices):
out_lines.append(price_to_min[prices[-1]][1])
else:
lo_p = prices[pos - 1]
hi_p = prices[pos]
d_lo = x - lo_p
d_hi = hi_p - x
if d_lo < d_hi:
out_lines.append(price_to_min[lo_p][1])
elif d_hi < d_lo:
out_lines.append(price_to_min[hi_p][1])
else:
lo_i, lo_s = price_to_min[lo_p]
hi_i, hi_s = price_to_min[hi_p]
out_lines.append(lo_s if lo_i < hi_i else hi_s)
sys.stdout.write("\n".join(out_lines))
if __name__ == "__main__":
main()
この解説は gpt-5.2-high によって生成されました。
投稿日時:
最終更新: