
配点: 450 点 / 実行時間制限: 2 sec / メモリ制限: 1024 MB / Difficulty: 1077 / NoviSteps: 1Q
問題概要
高橋君が 体のモンスターと順に戦うゲームで遊ぼうとしており、最初、高橋君の体力は
、魔力は
である。
番目に戦うモンスターに対して、高橋君は以下のどちらかの行動を選ぶことができる。
- 魔法を使わずに戦う。体力が
以上のときのみ選ぶことができ、体力が
減少しモンスターを倒す。
- 魔法を使って戦う。魔力が
以上のときのみ選ぶことができ、魔力が
減少しモンスターを倒す。
体全てのモンスターを倒すか、高橋君が行動できなくなるとゲーム終了である。ゲーム終了までに最大で何体のモンスターを倒せるか求めよ。
制約
- 入力は全て整数。
考察
この問題は、動的計画法によって解くことができる。
以下のような DP テーブルを考えよう。
番目までのモンスターを倒した時点で、合計消費体力が
であるときの合計消費魔力の最小値
初期値は
- 他は
とする。
番目のモンスターについて、
の順に、以下のように遷移する(貰うDP) :
- 魔法を使わずに倒す場合 : このモンスターについては消費体力が
、消費魔力が
なので、ここまでの消費魔力の合計は
である。ただし、
のときしか選べない。
- 魔法を使って倒す場合 : このモンスターについては消費体力が
、消費魔力が
なので、ここまでの消費魔力の合計は
である。ただし、
のときしか選べない。
この2パターンの最小値を採用する。つまり、
である。ただし、計算後に となったときは実現不可能なので
とする。
その後、 であるような
が存在しなければ、モンスター
は倒せなかったということなので、その時点で終了する。
空間計算量、時間計算量ともに となる。
実装例
#include <bits/stdc++.h> using namespace std; #define rep(i, start, end) for (auto i = (start); (i) < (end); (i)++) #define repe(i, start, end) for (auto i = (start); (i) <= (end); (i)++) constexpr int INF = 1e+9; // ======================================== // int main() { int N, H, M; cin >> N >> H >> M; vector<int> A(N), B(N); rep(i, 0, N) cin >> A[i] >> B[i]; vector<vector<int>> dp(N + 1, vector<int>(H + 1, INF)); dp[0][0] = 0; int ans = 0; repe(i, 1, N) { repe(h, 0, H) { int cost_1 = (h >= A[i - 1]) ? dp[i - 1][h - A[i - 1]] : INF; int cost_2 = (dp[i - 1][h] != INF) ? dp[i - 1][h] + B[i - 1] : INF; dp[i][h] = min(cost_1, cost_2); if (dp[i][h] > M) dp[i][h] = INF; } bool flag = false; repe(h, 0, H) { if (dp[i][h] != INF) flag = true; } if (flag) ans++; else break; } cout << ans << endl; return 0; }
実装時間: 35分
コメント
こういう DP の問題がきっちり処理できるとおもしろい。