【xdoj难题集】 1059: 英雄联盟

题目链接

这道题做了有一阵了,之前一直不知道是什么问题,今天又回头看了看,试了试精度,发现之前的如果末尾是5会无法进位,改完之后就对了。。。精度真的是玄学问题,这个问题一定要重视,还是要积累一些经验。

一道模拟题,题目看着很复杂,值的确是有点多,不过仔细读读发现其实要关注的并不多,可以精简为以下几个值。
1物理输出 由于护甲减少的是比例减少,所以被动技能加成也是直接乘以物理输出即可,所以那些攻击力护甲值什么的直接计算出物理输出。
2血量和最大血量 生命值,很重要,如果发现是0直接就死了
3吸血系数 单独储存 从而计算每次攻击后的回血量
4眩晕时间 单独储存
5被动技能相关量 包括之前的叠加层数和之前攻击时间

然后说说流程,两个部分,攻击和标记,先说简单的标记,每次遇到要标记的情况,就直接刷新这个人被眩晕的时间段即可,由于冷却时间最少5秒,所以不会再出现被之前的眩晕区间眩晕的情况,这个覆盖是成立的。这样那个人行动时如果发现处于眩晕的时间段就直接把这个行动平移到眩晕结束的时间即可。再说说相对复杂的攻击,因为攻击有可能是同时进行的,所以我的选择是把攻击和结算分开进行,每次如果攻击就在这个时间进行一次结算,不管几个人攻击都只结算一次,这样就不怕各种情况了。

具体实现的时候使用了优先队列,前面代表时间,后面代表行动类型,越大的标号代表相同时间越靠后的行动,另外时间因为最小单位是ms,没有更小的可能,所以直接用整数存储即可。输出的时候因为t到了百万级已经出现误差,我被迫手动四舍五入了一下,然后就没问题了,引以为戒啊。

贴代码

# include <stdio.h>
# include <queue>
# include <algorithm>

using namespace std;

typedef pair<int , int> P;//时间,行动 

int T;

double max(double a , double b)
{
    return (a > b) ? a : b;
}

double min(double a , double b)
{
    return (a < b) ? a : b;
}

void solve()
{
    priority_queue<P , vector<P> , greater<P> > que;
    //备注0 1 攻击 2 3 标记 4 结算

    double a[2];//攻击实际伤害
    int b[2] = {-1 , -1}, e[2] = {-1 , -1}, si[2];//下一次击晕时间 
    int ce[2] = {0}, ti[2] = {0};//叠加层数,上次攻击时间
    double hp[2], mhp[2];//生命值,最大生命值 
    double cov[2] = {0};//回血量

    int gj[2], xi[2], gc[2], bc[2], wk[2], rx[2], gs[2], lq[2]; 

    int i;
    for(i = 0 ; i < 2 ; i++) 
    {
        scanf("%lf %lf", &hp[i], &mhp[i]);
        scanf("%d %d %d %d %d %d %d %d", &gj[i], &xi[i], &gc[i], &bc[i], &wk[i], &rx[i], &gs[i], &lq[i]);
    } 

    for(i = 0 ; i < 2 ; i++)
    {
        a[i] = 100 * gj[i] / (100 + (double)wk[1 - i] * (100.0 - bc[i]) / 100.0 - gc[i]);
        si[i] = (100 - rx[i]) * 20;
    }

    que.push(P(0 , 0));
    que.push(P(0 , 1));
    que.push(P(0 , 2));
    que.push(P(0 , 3));

    bool f = 0;//是否结算
    while(!que.empty())
    {
        P y = que.top();
        que.pop();

        int n = y.second, t = y.first;

        //要是晕了就平移一下 
        if(b[n % 2] <= t && t < e[n % 2])
        {
            que.push(P(e[n % 2] , n));
            continue;
        }

        if(n < 2)//攻击 
        {
            if(ti[n] + 2000 <= t)
                ce[n] = 0;

            cov[n] = min((1 + 0.125 * ce[n]) * a[n] , hp[1 - n]) * ((double)xi[n] / 100);

            hp[1 - n] -= (1 + 0.125 * ce[n]) * a[n];

            ti[n] = t;
            if(ce[n] < 4)
                ce[n]++;
            que.push(P(t + gs[n] , n));

            if(!f)
            {
                f = 1;
                que.push(P(t , 4));
            }
        }
        else if(n < 4)
        {
            int num = n % 2;

            b[1 - num] = t + 1000;
            e[1 - num] = b[1 - num] + si[1 - num]; 

            que.push(P(t + 1000 * lq[num] , n));
        }
        else
        {
            f = 0;

            if(hp[0] > 0)
                hp[0] = min(cov[0] + hp[0] , mhp[0]);
            cov[0] = 0;

            if(hp[1] > 0)
                hp[1] = min(cov[1] + hp[1] , mhp[1]);
            cov[1] = 0;

            //被这步坑惨了 
            if(t % 10 >= 5)
                t += 10 - t % 10; 

            if(hp[0] <= 0 || hp[1] <= 0)
            {
                printf("%.2lf %.2lf %.2lf\n", (double)t / 1000, max(0.00 , hp[0]), max(0.00 , hp[1]));
                return;
            }
        }
    }
}

int main()
{
    scanf("%d", &T);

    while(T--)
    {
        solve();
    }

    return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值