Official
D - 省エネ照明計画 / Energy-Saving Lighting Plan Editorial
by
D - 省エネ照明計画 / Energy-Saving Lighting Plan Editorial
by
MMNMM
この問題は、ナップサック DP に「直前の要素を選んだか」の場合分けを加えることで解くことができます。
具体的には、\(\mathrm{dp} _ i[j]\coloneqq\) 街路灯 \(i\) までを交換費用が \(j\) 円以下になるように交換するときの電気代削減効果の最大値 として、
\[\mathrm{dp} _ i[j]=\begin{cases}0&(i\le0\vee j\le 0)\\\max\lbrace\mathrm{dp} _ {i-1}[j],\mathrm{dp} _ {i-2}[j-W _ i]+V _ i\rbrace&(\textrm{otherwise})\end{cases}\]
と計算することができます(一つ目は街路灯 \(i\) を交換しない場合、二つ目は交換する場合に対応します)。
これを実装することで、\(O(NK)\) 時間でこの問題を解くことができます。 DP テーブルを使いまわすことで空間計算量を \(O(NK)\) から \(O(K)\) にすることもできます。
実装例は以下のようになります。
#include <iostream>
#include <vector>
using namespace std;
int main() {
int N, K;
cin >> N >> K;
vector<long> dp(K + 1), prev(K + 1);
for (int i = 0; i < N; ++i) {
int V, W;
cin >> V >> W;
swap(dp, prev);
// 交換する場合
for (int j = K - W; j >= 0; --j) {
dp[j + W] = max(dp[j + W], dp[j] + V);
}
// 交換しない場合
for (int j = 0; j <= K; ++j) {
dp[j] = max(dp[j], prev[j]);
}
}
cout << dp[K] << endl;
return 0;
}
N, K = map(int, input().split())
dp = [0 for i in range(K + 1)]
prev = [0 for i in range(K + 1)]
for i in range(N):
V, W = map(int, input().split())
dp, prev = prev, dp
# 交換する場合
for j in range(K - W, -1, -1):
dp[j + W] = max(dp[j + W], dp[j] + V)
# 交換しない場合
for j in range(K + 1):
dp[j] = max(dp[j], prev[j])
print(dp[K])
posted:
last update:
