1 条题解
-
0
思路与算法
设S[j]为累计伤害。若上次施法在t,此后j时刻伤害结算后的生命为hp-S[j]+15t。固定冷却CD,DP枚举可达施法时刻t,再枚举生命仍为正的后续时刻j,满足间隔则可施法;t=0表示尚未施法,不限制第一次。若能活过n则CD可行,利用单调性二分最大CD;CD=n可行表示至多一次施法,输出无穷。
正确性说明
所有积蓄的治疗在施法时一并释放,至t累计治疗恰为15t,与此前施法划分无关,所以只记录最后时刻足够。扫描遇生命≤0便不能再挽救,DP恰好枚举所有合法施法序列。增大CD只删合法序列,二分正确。
复杂度
每次判定O(n²),二分O(logn),读入生命字符串长L,总O(L+n²logn)时间,O(n)空间;前缀和与DP初始化计入。
易错点
先受伤后治疗,生命0已经死亡。初始生命可极长,正伤总量≤500000,截为500001保持生存判断。最小CD也不可行输出-1,无穷需单独判断。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; string h; cin >> n >> h; int hp = 0; for (char c : h) hp = min(500001, hp * 10 + c - '0'); vector<int> s(n + 1); for (int i = 1, x; i <= n; i++) { cin >> x; s[i] = s[i - 1] + x; } auto feasible = [&](int cd) { vector<int> can(n + 1); can[0] = 1; for (int t = 0; t <= n; t++) if (can[t]) { bool alive = true; for (int j = t + 1; j <= n; j++) { if (hp - s[j] + 15 * t <= 0) { alive = false; break; } if (t == 0 || j - t >= cd) can[j] = 1; } if (alive) return true; } return false; }; if (feasible(n)) { cout << "No upper bound.\n"; return 0; } if (!feasible(1)) { cout << -1 << '\n'; return 0; } int l = 1, r = n - 1; while (l < r) { int m = (l + r + 1) / 2; if (feasible(m)) l = m; else r = m - 1; } cout << l << '\n'; }
- 1