链接:The 18th Heilongjiang Provincial Collegiate Programming Contest
Problem K. Turn-based Game
题意
主角有 A A A 血量,每只怪物有 B B B 血量,每个单位每次进攻伤害为 1 1 1,每个单位血量低于等于 0 0 0 判定为死亡。 进行 n n n 轮试炼,每轮有 a i a_i ai 只怪物,每一轮试炼都是主角先攻击,在主角的每次攻击后场上所有还存活的怪物对主角一起发动一次攻击。每次主角的攻击可能得到朋友的帮助,存在一个长度为 m m m 序列 b i b_i bi 代表前 m m m 次攻击中 第 i i i 次攻击,若 b i = 1 b_i=1 bi=1 主角若杀死一只怪物就可以立刻发动一次攻击(即两次攻击后怪物再攻击)。每轮试炼主角的攻击次数都从 1 1 1 开始计算。
问 n n n 轮试炼后主角最多剩下多少血量,若死亡则输出 ““LOSE”.
思路
首先我们要认识到两个点:对于 n n n 轮试炼,每轮之间互不影响,并且对于最优策略而言,怪物的数量没有关系,无论是几只怪物,只和我的攻击次数和朋友的帮助有关系。
我们来考虑一个简单的问题,如何用dp求解不存在朋友帮助的普通情况。
由于每次怪兽群体的总伤害量是随数量变化的,所以每次攻击后受到的伤害是不同的,这样不利于我们求解,所以我们在dp过程中计算的是每杀死一只怪物那么接下来受到的伤害就少 1 1 1. 这样就不论初始怪兽数量是多少,我们只需要知道攻击次数 i i i 和 减少的伤害就可以算出最终的结果。
定义dp方程: f [ i ] [ j ] f[i][j] f[i][j] 代表攻击 i i i 次,杀死 j j j 只怪兽时最多可以少受到的伤害。 a n s = s u m ∗ i − f [ i ] [ j ] ans = sum * i - f[i][j] ans=sum∗i−f[i][j].
接下来考虑特殊情况,只需要将得到朋友帮助可以立刻发动一次攻击考虑进去,立刻发动攻击对结果的影响是什么呢?就是这一次发动的这一次攻击不用受到怪兽的伤害,那么结合上面普通情况的方程,我们只需要增加一维,设受到朋友帮助 k k k 次, a n s = ( i − k ) ∗ s u m − f [ i ] [ j ] [ k ] ans = (i - k) * sum - f[i][j][k] ans=(i−k)∗sum−f[i][j][k].
最后对于每一轮战斗,我们只需要怪兽初始数量带入,并枚举受到朋友帮助的次数即可计算出结果。
具体见代码。
代码
#include <bits/stdc++.h>
using namespace std;#define ll long long
const int N = 1e5 + 10, inf = 0x3f3f3f3f;int A, B, n, m;
int a[N], b[N];int f[2010][110][30]; // 前i次攻击,杀死j个怪物,使用了k次帮助的最小代价(算的是可以减少的代价,总的代价最后再加上即可)
int ans[110];
int main(){ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);cin >> A >> B;cin >> n >> m;for(int i = 1; i <= n; i ++) cin >> a[i];for(int i = 1; i <= m; i ++) cin >> b[i];memset(f, 0x3f, sizeof f);f[0][0][0] = 0; for(int i = 1; i <= 100 * B; i ++){for(int j = 0; j < 100; j ++){for(int k = 0; k < 20; k ++){if(f[i - 1][j][k] == inf) continue ;int sum = i - j * B; // 总伤害 - 杀死的怪兽 = 剩余伤害量if(sum >= B){ // 剩余伤害足够杀死一个怪兽if(b[i] == 1) f[i][j + 1][k + 1] = min(f[i][j + 1][k + 1], f[i - 1][j][k]); // 因为本轮攻击可以看做是被朋友帮助发动的,不会受到伤害就不用减去死去怪物数量else f[i][j + 1][k] = min(f[i][j + 1][k], f[i - 1][j][k] - (j + 1));}f[i][j][k] = min(f[i][j][k], f[i - 1][j][k] - j); // 本次不杀死新的怪兽,那么已经被杀死的j只怪兽无法攻击从代价中减去}}}for(int i = 1; i <= 100; i ++){ans[i] = inf;for(int k = 0; k <= min(i, 20); k ++){if(f[i * B][i][k] < inf){ans[i] = min(ans[i], (i * B - k) * i + f[i * B][i][k]);}}}ll sum = 0;for(int i = 1; i <= n; i ++){sum += ans[a[i]];}if(sum > A) cout << "LOSE\n";else cout << A - sum << "\n";return 0;
}