CDOJ 28 补兵(kill)

在非常流行的DOTA游戏中,补兵是非常重要的一种技术统计。如果一个单位被对方的多个单位攻击至死,则对该单位造成最后一次(致命的)伤害的攻击者将会获得更多的奖励(金钱和经验),这名攻击者被记录一次补兵。

现在假设有多个人攻击对方的一个单位,而你是其中的一个,你并不想在前面出手攻击,而只想打一次就直接将对方打死,完成一次补兵。假设其他人每次攻击的伤害和每两次攻击之间的间隔都是固定的,而你的攻击伤害也是固定的。输入将给出每个人两次攻击之间的间隔时间,并假设每个人第一次攻击的时刻值就是他的两次攻击之间的时间间隔值。例如,一个人攻击间隔为2,则他会在时刻2468……进行攻击。

时间以整数计算。

如果多个人同时攻击导致对方死亡,攻击伤害最大的那个被记录一次补兵。如果你是攻击伤害最大的之一(还有其他和你攻击伤害一样的,但没有更大的),则你被记录一次补兵。

一个单位血量小于等于0就被判为死亡。

你的任务是求出合适的攻击时间段,使得这次补兵能够完成。这个时间段一定是一个区间,你需要输出的是它的两个端点。

Input

输入包含多组数据。每组数据第一行是一个整数N(2N1000),表示攻击对方某个单位的人数。N=0表示输入结束。接下来有N1行,每行两个整数,分别表示每个人每次攻击的伤害A(1A10),以及每两次攻击之间的间隔T(1T100)。最后有一行包含两个整数,分别表示你的攻击伤害P(1P100000)以及对方单位的血量H(P<H1000000)。

Output

对每组数据,输出一行,如果不能完成补兵,输出Impossible,否则输出最早可以完成补兵的时间和最晚可以完成补兵的时间,用空格间隔。

Sample input and output

Sample InputSample Output
2
2 2
3 10
2
20 2
3 10
0
8 10
Impossible

Hint

输入数据较多,尽量用scanfprintf代替cincout

Source

电子科技大学第六届ACM程序设计大赛 初赛
 
看到这道题第一想法就是暴力求解,模拟时间,看了范围觉得不行。
因为在模拟时间时有很多时间点是不用模拟的,比如大于H/A*B的范围。
于是用一点数学期望的想法,对每个数据A/B,假设每一个都是每秒攻击A/B,此时可以算出区间为((H-P)/∑(A/B),H/∑(A/B)),
而这个区间是不对的,左右边界都应向右移,(因为每次攻击只在整数倍的B),这时候再来模拟时间找出左边界和右边界,就可以了。
这样做还没完,因为还要保证补兵是最大攻击,所以还要计算此时的边界是否是比P大的A的整数倍,如果是,边界就往中间移。
如此,就AC了。
 
#include <iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int A[1005],B[1005],C[1005],N,P,H,d;
bool panduan(int m)
{
    for (int j=1;j<=d;j++)
        if ((m%B[C[j]]==0))
            return true;
    return false;
}
int main()
{
    scanf("%d",&N);
    while (N!=0){
        int j;
        double ave=0;
        memset(A,0,sizeof(A));
        memset(B,0,sizeof(B));
        memset(C,0,sizeof(C));
        for (j=1;j<N;j++){
            scanf("%d %d",A+j,B+j);
            ave+=A[j]*1.0/B[j];     //这个地方坑了我3次提交。。。由于没有1.0而ave变成0,造成下面有0做除数。。。
        }
        scanf("%d %d",&P,&H);
        d=0;
        for (j=1;j<N;j++)
            if (A[j]>P){
                d++;
                C[d]=j;
        }
        int g=0,h=0;
        g=(int)(H/ave);
        h=(int)((H-P)/ave);
        int l=0;
        for (j=1;j<N;j++) l=l+g/B[j]*A[j];
        while (l<H){
            g++;
            l=0;
            for (j=1;j<N;j++) l=l+g/B[j]*A[j];
        }
        while (panduan(g)&&(g>=h)) g--;
        l=0;
        for (j=1;j<N;j++) l=l+h/B[j]*A[j];
        while (l<H-P){
            h++;
            l=0;
            for (j=1;j<N;j++) l=l+h/B[j]*A[j];
        }
        while (panduan(h)&&(h<=g)) h++;
        if (h>g) printf("Impossible\n");else printf("%d %d\n",h,g);
        scanf("%d/n",&N);
    }
    return 0;
}

 

提交了4次。。。
2700Atlantis6728Accepted1212 KB12 MSC++1280 B
28 minutes ago
2697Atlantis6728Time Limit Exceeded on test 1  C++1273 B
42 minutes ago
2695Atlantis6728Time Limit Exceeded on test 1  C++1257 B
50 minutes ago
2692Atlantis6728Time Limit Exceeded on test 1  C++1098 B 
 
 

转载于:https://www.cnblogs.com/Atlantis67/p/3612589.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值