CDOJ 28 补兵(kill) 解题报告
写博客什么的真是……不想动……之前那个没怎么用过的域名已经报废了,今晚倒腾域名折腾了好久。
题目链接http://acm.uestc.edu.cn/#/problem/show/28
一开始我的打算是直接计算出开始和结束的那个点。
既然说了一开始,就说明我失败了……
大概能想到的就是求出每一秒的平均伤害,当然,这对于我的想法并没有什么卯月,只能大致求出一个值,而且我们还不能直接计算出结束点是在我们用平均值计算出来那个点的前面还是后面——可以推算出的就是s肯定在后面,不过我是看了别人的题解后的马后炮。
是的,没错,我去查了题解,看完别人代码后……MDZZ,我居然连暴力都不会打了……
解题方法是先求每一秒的平均值,用这个算出可以开始补兵的时间(我设为s)和应该结束补兵的时间(我设为t),自然,这两个值是不准确的。对于无论是s还是t来说,用每秒平均值计算都可能会出现伤害算多了的情况。我们在得到s和t的近似值之后,向后计算直到s和t均满足各自的要求,也就是对s来说敌军所受的伤害再加上一次自己补兵伤害就会死亡,对于t来说,敌军刚好死亡。
然后再考虑同时攻击的时候己方有人伤害比自己高的情况,这个也是暴力求出。对于每一秒扫一遍,不符合就往中间靠拢。当然,出现s > t的时候就说明不可能存在这个区间,输出Impossible即可。
顺便试试新的代码高亮。
#include <cstdio>
using namespace std;
int N;
int A[10005], T[10005];
int P, H;
double dmg;
int totaldmg;
int s, t;
int nextInt() {
char c; while ((c = getchar()) < '0' || c > '9'); int r = c - '0';
while ((c = getchar()) >= '0' && c <= '9') (r *= 10) += c - '0';
return r;
}
bool conflict(int time) {
for (int i = 1; i < N; ++i)
if (!(time % T[i]) && A[i] > P)
return true;
return false;
}
int main() {
while (N = nextInt()) {
dmg = 0;
for (int i = 1; i < N; ++i) {
A[i] = nextInt(), T[i] = nextInt();
dmg += (double)A[i] / T[i];
}
P = nextInt(); H = nextInt();
s = (H - P) / dmg; t = H / dmg;
// 暴力扫出起点s
totaldmg = 0;
for (int i = 1; i < N; ++i)
totaldmg += s / T[i] * A[i];
while (totaldmg + P < H) {
++s;
for (int i = 1; i < N; ++i)
if (!(s % T[i])) totaldmg += A[i];
}
while (s <= t && conflict(s)) ++s;
// 暴力扫出终点t
totaldmg = 0;
for (int i = 1; i < N; ++i)
totaldmg += t / T[i] * A[i];
while (totaldmg < H) {
++t;
for (int i = 1; i < N; ++i)
if (!(t % T[i])) totaldmg += A[i];
}
while (s <= t && conflict(t)) --t;
if (s <= t) printf("%d %d\n", s, t); else puts("Impossible");
}
return 0;
}