题目大意
题意其实很清楚,就是一个模拟对战的游戏。游戏有两个角色 A、B,A 有 hpa 的血量,攻击力为 x;B 有 hpb 的血量,攻击力为 y。
A 每回合有两种操作选择:(1)攻击。对 B 造成 x 点伤害;(2)回血。消耗一瓶血瓶,回复 d 点血。
B 每回合只能执行一种操作:攻击。对 A 造成 y 点伤害。
现在问:A 是否可以通过合理的操作获得胜利?如果可以,输出“ Y ”,否则,输出“ N ”。
思路
我们可以想到,回血是这道题的关键。A 相对与 B 的优势就是可以选择是否回血。
那么可以分类讨论一下:
(1)\(d \le y\),A 一次回血量少于等于 B 的攻击力。
因为 A 每回合只能执行一次操作,如果他回血了就无法攻击。如果 A 的回血量少于 B 的攻击力,那么只要 A 执行回血的操作,然后再被 B 攻击,A 的血量会减少或不变。这样子是没有收益的。
那么在这种情况下,A 只能一直执行攻击的操作才能最大可能的获得胜利。
胜利条件取决于哪一方可以先把对方打死。
只需计算两方以自己的攻击力需要几个回合才能把对方打死,在进行判断即可。
if(d <= y){//下面这种先加上除数 - 1 再去除以除数的操作是为了向上取整。int numa = (hpa + y - 1) / y; // A 打死 B 所需的回合数。int numb = (hpb + x - 1) / x; // B 打死 A 所需的回合数。if(numa >= numb) flag = 1; // 只要 A 的回合数 <= B 的回合数,就说明 A 可以获胜。else flag = 0;
}
(2) \(d > y\), A 一次回血量大于 B 的攻击力。
这时候,A 进行回血操作才有意义。
我们先假设 A 可以打死 B,那么 A 至少要攻击 $K = \left \lceil hpb / x \right \rceil $ ,除了最后一个回合不受 B 的攻击,其余回合都会受到 B 的攻击,即受攻击 \(K- 1\).在这个过程中假设 A 为了活下来使用了 r 次回血。
那么假设成立的条件是什么呢?
就是在这个过程中始终 $ hpa > 0 $,且最终 $ r \le cnt$。
如此,我们就很清晰了。直接模拟上述过程。记录过程中的血量是否小与零,记录使用了多少次回血。
int K = (hpb - x - 1) / x;
int m = K - 1; // A 只需活着经过 K - 1 次攻击轮次就可以,因为 A 最后一次轮不会受到 B 的攻击
int Hpa = hpa, r = 0, attacksDone = 0; // attacksDone 记录 A 攻击的次数
while(attacksDone < m && r <= cnt){if(Hpa <= 0) break;int L = (Hpa - 1) / y; // 算出 A 当前血量可以被 B 攻击几次而不需要回血 if(L == 0){Hpa = min(Hpa + d, hpa) - y;r ++;}else{if(attacksDone + L >= m){attacksDone += L;break;}else{attacksDone += L;Hpa -= L * y;Hpa = min(Hpa + d, hpa) - y;r ++;}}
}
if(attacksDone >= m && r <= cnt) flag = 1;
else flag = 0;
完整代码
代码有点多,但不要别吓到哦。实际有用的代码就只有 solve() 函数里的东西,外面的只是我的板子。
然后你们自己写的时候要开long long哦,我这里是#define int long long
了。
点击查看代码
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;using PII = pair<int, int>;#define fi first
#define se second
#define pb push_back
#define all(a) a.begin(), a.end()
#define lowbit(x) (x & (-x))
#define debug(x) cout << #x << " = " << x << "\n";
#define vdebug(a) cout << #a << " = "; for(auto& x: a) cout << x << " "; cout << "\n";const int N=2e5+10,M=1010;
const int mod=1e9+7,MOD=998244353;
const int INF=0x3f3f3f3f;
const int inf=0x3f3f3f3f3f3f3f3f;
const int dx[]={0,0,1,-1},dy[]={1,-1,0,0};int n,m,k;void solve(){int hpa, hpb, x, y, d, cnt;// cin >> hpa >> hpb >> x >> y >> d >> cnt;scanf("%lld %lld %lld %lld %lld %lld",&hpa, &hpb, &x, &y, &d, &cnt);int flag = 0;if(hpb - x <= 0){flag = 1;}else if(hpa - y <= 0){flag = 0;}else if(d <= y){int numa = (hpa + y - 1) / y; // A 打死 B 所需的回合数 int numb = (hpb + x - 1) / x; // B 打死 A 所需的回合数 if(numa >= numb) flag = 1; // 只要 A 的回合数 <= B 的回合数,就说明 A 可以获胜。 else flag = 0;}else{int K = (hpb - x - 1) / x;int m = K - 1; // A 只需活着经过 K - 1 次攻击轮次就可以,因为 A 最后一次轮不会受到 B 的攻击 int Hpa = hpa, r = 0, attacksDone = 0; // attacksDone 记录 A 攻击的次数 while(attacksDone < m && r <= cnt){if(Hpa <= 0) break;int L = (Hpa - 1) / y; // 算出 A 当前血量可以被 B 攻击几次而不需要回血 if(L == 0){Hpa = min(Hpa + d, hpa) - y;r ++;}else{if(attacksDone + L >= m){attacksDone += L;break;}else{attacksDone += L;Hpa -= L * y;Hpa = min(Hpa + d, hpa) - y;r ++;}}}if(attacksDone >= m && r <= cnt) flag = 1;else flag = 0;}// if(flag) cout << "Y" << endl;// else cout << "N" << endl;if(flag) printf("Y\n");else printf("N\n");
}signed main(){
// ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);int _=1;cin>>_;while(_--) solve();return 0;
}