艾雅法拉的点燃
艾雅法拉的点燃
三维dp
1. 问题描述
共有 \(n\) 名敌人从左到右排成一列,第 \(i\) 名敌人的初始生命值为 \(a_i\)。
若一名敌人的当前生命值大于 \(0\),则称其仍然存活。
艾雅法拉可以进行若干次行动,每次行动选择一名当前仍然存活的敌人作为目标,选择一种攻击方式执行:
- 普通攻击:消耗 \(x\) 点法力,使目标敌人的生命值减少 \(1\)。
- 技能“点燃”:消耗 \(y\) 点法力,使目标敌人的生命值减少 \(2\),并使它左右相邻的敌人生命值各减少 \(1\)。若某一侧不存在相邻敌人,则该侧不会造成伤害。
敌人无论存活与否,位置固定不变,相邻关系始终为初始数组中的左右位置。敌人的生命值可以被降到 \(0\) 以下。
求出使所有敌人都不再存活所需消耗的最小法力值。
输入格式
- 第一行三个整数 \(n, x, y\)(\(1 \le n \le 2000,\ 1 \le x, y \le 10^9\))。
- 第二行 \(n\) 个整数 \(a_1, a_2, \dots, a_n\)(\(1 \le a_i \le 50\))。
输出格式
- 输出一个整数,表示最小法力消耗。
2. 核心思路
技能“点燃”的波及范围仅限左右各 \(1\) 格,因此第 \(i\) 个敌人受到的伤害只由以下三者决定:
- 自身被释放“点燃”的次数;
- 左侧邻居(第 \(i-1\) 个)释放“点燃”的波及;
- 右侧邻居(第 \(i+1\) 个)释放“点燃”的波及。
由于 \(a_i \le 50\),且每次“点燃”至少造成 \(2\) 点伤害,所以对同一个敌人释放“点燃”的次数超过 \(50\) 绝无必要(\(2 \times 51 > 50\),徒增消耗)。因此我们可以将每个敌人被释放“点燃”的次数限制在 \([0, 50]\) 内枚举。
采用延迟结算的策略:当我们处理到第 \(i\) 个敌人时,第 \(i-1\) 个敌人的所有伤害来源(自身、左、右)已经全部确定,此时可以结算第 \(i-1\) 个敌人,并用普通攻击补足剩余生命值。这样就将问题转化为一个线性动态规划。
3. 动态规划设计
3.1 状态定义
设 dp[i][j][k] 表示:
已经处理完前 \(i-1\) 个敌人(即前 \(i-1\) 个敌人已确定死亡,且不再受后续影响),
并且第 \(i-1\) 个敌人被释放“点燃”的次数为 \(j\),
第 \(i\) 个敌人被释放“点燃”的次数为 \(k\),
此时所消耗的最小法力值。
其中 \(0 \le j, k \le 50\)。
3.2 状态转移
当我们从 dp[i-1][p][j] 转移到 dp[i][j][k] 时,我们需要枚举第 \(i-2\) 个敌人的“点燃”次数 \(p\)(\(0 \le p \le 50\))。此时,第 \(i-1\) 个敌人的总伤害已经可以由 \(p, j, k\) 完全确定:
- 自身被点燃 \(j\) 次,造成 \(2 \times j\) 点伤害;
- 左侧邻居(第 \(i-2\) 个)释放 \(p\) 次点燃,波及到它,造成 \(p\) 点伤害;
- 右侧邻居(第 \(i\) 个)释放 \(k\) 次点燃,波及到它,造成 \(k\) 点伤害。
总伤害为:
若 \(\text{damage} < a_{i-1}\),则需要用普通攻击补足,补刀次数为:
补刀消耗为 \(\text{need} \times x\)(若 \(\text{damage} \ge a_{i-1}\) 则无需补刀)。
因此转移方程为:
注意:这里 \(dp[i-1][p][j]\) 已经包含了第 \(i-2\) 个敌人及之前的所有消耗,也包含了释放第 \(i-1\) 个敌人 \(j\) 次“点燃”的消耗(因为 \(j\) 是状态的一部分,在之前转移时已计入)。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve() {
ll n, x, y; cin >> n >> x >> y;
vector<int> a(n + 1);
for(int i = 1; i <= n; i ++) cin >> a[i];
if(n == 1) {
ll ans;
if(y < x * 2) ans = (a[1] / 2 * y + a[1] % 2 * x);
else ans = a[1] * x;
cout << ans << '\n';
return;
}
vector<vector<vector<ll> > > dp(n + 1, vector<vector<ll>>(51, vector<ll>(51, 1e15)));
for(int i = 0; i <= 50; i ++) {
for(int j = 0; j <= 50; j ++) {
ll cost = i * y;
if(i * 2 + j < a[1]) cost += x * (a[1] - i * 2 - j);
dp[2][i][j] = cost;
}
}
for(int i = 3; i <= n; i ++) {
for(int j = 0; j <= 50; j ++) {
for(int k = 0; k <= 50; k ++) {
for(int p = 0; p <= 50; p ++) {
ll cost = j * y;
if(j * 2 + k + p < a[i - 1]) cost += x * (a[i - 1] - j * 2 - p - k);
dp[i][j][k] = min(dp[i][j][k], dp[i - 1][p][j] + cost);
}
}
}
}
ll ans = 1e15;
for(int i = 0; i <= 50; i ++) {
for(int j = 0; j <= 50; j ++) {
ll cost = j * y;
if(j * 2 + i < a[n]) cost += x * (a[n] - j * 2 - i);
ans = min(ans, dp[n][i][j] + cost);
}
}
cout << ans << endl;
}
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr);
solve();
return 0;
}

浙公网安备 33010602011771号