公式
B - 電気自動車の旅 / Journey of an Electric Vehicle 解説 by admin
Claude 4.5 Opus概要
電気自動車が \(N\) 個の区間を走行する際、途中の充電ステーションでバッテリーを交換するかどうかを適切に選択し、全区間を走破できるか判定する問題です。
考察
重要な気づき
充電ステーションでの選択は貪欲に決まる
- バッテリーを交換すると残量が \(S_j\) になります
- 現在の残量より \(S_j\) が大きければ交換した方が得、そうでなければ交換しない方が得です
- なぜなら、バッテリー残量が多いほど先に進める可能性が高くなるからです
シミュレーションで十分
- 区間を順番に走行しながら、充電ステーションがあれば交換を検討するだけでOK
- 各区間を走行する前にバッテリー残量が \(1\) 以上あるか確認します
具体例で理解する
例えば \(N=3\)、\(K=5\)、\(D=[3, 4, 2]\) で、区間1と区間2の間(\(P=1\))に \(S=6\) のステーションがある場合:
- 区間1を走行: バッテリー \(5 \to 5-3=2\)
- ステーション到着: 現在 \(2\) < \(S=6\) なので交換 → バッテリー \(6\)
- 区間2を走行: バッテリー \(6 \to 6-4=2\)
- 区間3を走行: バッテリー \(2 \to 2-2=0\)
- 全区間走破成功!
なぜ貪欲法で正しいか
- 一度通り過ぎたステーションには戻れません
- 現在の残量より大きい \(S_j\) を持つステーションで交換しないと、その分の「お得さ」を失います
- 逆に、現在より小さい \(S_j\) に交換すると残量が減るだけで損です
アルゴリズム
- 初期バッテリー残量を \(K\) に設定
- 区間 \(1\) から \(N\) まで順番に処理:
- 区間 \(i\) を走る前に、その手前にある充電ステーション(\(P_j = i-1\))があれば確認
- 現在のバッテリー残量より \(S_j\) が大きければ交換
- 走行前にバッテリー残量が \(1\) 未満なら
Noを出力して終了 - 区間を走行し、バッテリー残量を \(D_i\) だけ減らす
- 全区間を走り終えたら
Yesを出力
[スタート] --区間1--> [ステーション?] --区間2--> [ステーション?] --区間3--> [ゴール]
K P=1にあれば P=2にあれば
計算量
時間計算量: \(O(N + M)\)
- 各区間を1回ずつ処理:\(O(N)\)
- 各充電ステーションを1回ずつ確認:\(O(M)\)
空間計算量: \(O(N + M)\)
- 配列 \(D\) の格納:\(O(N)\)
- 充電ステーション情報の格納:\(O(M)\)
実装のポイント
インデックスの対応に注意
- 問題文では区間は1-indexedですが、配列は0-indexedです
- 「区間 \(i\) と区間 \(i+1\) の間」にあるステーションは、0-indexedで区間 \(i\) を走り終わった後に処理します
走行の判定タイミング
- 「走行する直前にバッテリー残量が \(1\) 以上」であれば走行可能
- 走行後にバッテリーが \(0\) 以下になっても、次の区間の前に \(1\) 以上あれば問題ありません
- 最後の区間を走り終えた後は、バッテリーが \(0\) 以下でもOKです
ステーションがない場合
\(M = 0\) の場合も正しく動作するよう、ループ内で
station_idx < Mのチェックを忘れずにソースコード
def solve():
N, M, K = map(int, input().split())
D = list(map(int, input().split()))
stations = []
for _ in range(M):
P, S = map(int, input().split())
stations.append((P, S))
# 現在のバッテリー残量
battery = K
# 充電ステーションのインデックス
station_idx = 0
for i in range(N):
# 区間 i+1 を走行する前に充電ステーションがあるかチェック
# 区間 i+1 の手前 = 区間 i と区間 i+1 の間
# つまり P_j = i のとき、区間 i と区間 i+1 の間にステーションがある
# 区間は1-indexedなので、区間 i+1 を走る前にチェックするのは P_j = i
# i は 0-indexed で、区間番号は 1-indexed
# 区間 i (0-indexed) を走る = 区間 i+1 (1-indexed) を走る
# 区間 i+1 (1-indexed) の手前にあるステーションは P_j = i (1-indexed)
# 区間 1 の手前にはステーションがないので、最初の区間の前はスキップ
# 区間 i+1 (1-indexed) を走る前に、P_j = i の位置にあるステーションをチェック
if i > 0:
# 区間 i (1-indexed, つまり D[i-1]) を走り終わった後、
# 区間 i+1 (1-indexed, つまり D[i]) を走る前
# ステーション位置 P_j = i (1-indexed) をチェック
while station_idx < M and stations[station_idx][0] == i:
P, S = stations[station_idx]
# バッテリーを交換するかどうか選択
# 交換すると battery が S になる
if S > battery:
battery = S
station_idx += 1
# 区間 i+1 (1-indexed) を走行
# 走行前に battery >= 1 である必要がある
if battery < 1:
print("No")
return
# 走行後、バッテリーが D[i] 減少
battery -= D[i]
# バッテリーが 0 以下になっても、次の区間を走る前に 1 以上あればOK
# ただし最後の区間を走り終わった後は関係ない
# すべての区間を走行完了
# 走行後にバッテリーが0以下でも、すべての区間を走り終えたのでOK
print("Yes")
solve()
この解説は claude4.5opus によって生成されました。
投稿日時:
最終更新: