艾雅法拉的点燃

艾雅法拉的点燃

三维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} = 2j + p + k \]

若 \(\text{damage} < a_{i-1}\),则需要用普通攻击补足,补刀次数为:

\[\text{need} = a_{i-1} - \text{damage} \]

补刀消耗为 \(\text{need} \times x\)(若 \(\text{damage} \ge a_{i-1}\) 则无需补刀)。

因此转移方程为:

\[dp[i][j][k] = \min_{0 \le p \le 50} \Big( dp[i-1][p][j] + \max(0,\ a_{i-1} - 2j - p - k) \times x \Big) \]

注意:这里 \(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;
}
posted @ 2026-10-03 10:12  thenrify  阅读(10)  评论(0)    收藏  举报