首页 > 试题广场 >

Pokemon

[编程题]Pokemon
  • 热度指数:3988 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
牛牛是励志成为世界第一宝可梦大师的宝可梦训练家。现在他遇到了一个强劲的野生宝皮卡丘,野生宝皮卡丘的生命值是HP,攻击力是ACK,牛牛召唤的宝可梦是杰尼龟。杰尼龟的生命值是HP2,攻击力是ACK2,除此之外身为训练家还可以给宝可梦吃药让他满血复活(吃药发生在双方发动攻击之前,并且吃药一方不得在本回合发动攻击)。
牛牛想知道他最少多少回合才能能打败野生宝皮卡丘?
因为皮卡丘会电光一闪,所以皮卡丘总是比杰尼龟先发动攻击。若能成功击败野生皮卡丘则返回最少回合数,否则返回-1。
示例1

输入

8,3,8,1

输出

14

说明

至少需要14回合战胜野生皮卡丘
示例2

输入

1,1,1,1

输出

-1

说明

皮卡丘先出招就直接打死了杰尼龟,所以无法获胜

备注:
 

首先是特判,如果初始时候皮卡丘攻击力大于等于杰尼龟的生命值,那么杰尼龟一直处于死亡复活的死循环中,直接返回 -1

接下来,以例子(15 3 10 2)说明:

杰尼龟               皮卡丘
10 -> 7             15 -> 13
7 -> 4              13 -> 11
4 -> 1              11 -> 9
1 -> 10 -> 7        9
7 -> 4              9 -> 7
4 -> 1              7 -> 5
1 -> 10 -> 7        5
7 -> 4              5 -> 3
4 -> 1              3 -> 1
1 -> 10 -> 7        1
7 -> 4              1 -> GG

没有直接返回,说明杰尼龟抗住了第一次攻击,并且发起了一次反击,皮卡丘的生命值降低,变为 HP = HP - ACK2

所以,可以声明 result 记录一共需要的回合数,同时初始化 result 为 1,表示第一次攻击。

然后,这里有几个点需要特判:

  • 第一次攻击之后,如果皮卡丘生命值小于等于 0,说明皮卡丘被杰尼龟反杀,直接返回 1
  • 此外,第一次攻击之后,如果杰尼龟剩余生命值小于等于皮卡丘攻击力,那么杰尼龟同样处于死亡复活的死循环中,直接返回 -1

再来看除第一次攻击之外的其余的部分,容易得到这部分存在循环(也就是 1 -> 7 -> 4 -> 1 -> ···):

  • 每个循环的回合数为 countOfCycle = (HP2 - 1) / ACK
  • 每个循环都有一次复活过程不可以攻击,所以每个循环中实际有效的总攻击力为 ackOfCycle = (countOfCycle - 1) * ACK2

这样就可以计算出“至少”需要几轮才可以打败皮卡丘,即 basicCount = HP / ackOfCycle

知道了“至少”需要几轮之后,可以将这部分直接接到 result 上,即 result += basicCount * countOfCycle

需要注意的是,计算 basicCount 时默认是向下取整,那么还要判断皮卡丘是否还有血,即 leftHP = HP % ackOfCycle 是否为 0

  • 如果 leftHP 为 0,说明正好结束掉皮卡丘,但是也增加了一次复活,需要减去
  • 如果 leftHP 不为 0,说明皮卡丘还有血,需要计算出还需要几个回合,也就是 result += leftHP / ACK2 + (leftHP % ACK2 == 0 ? 0 : 1)
public long pokemonFight(long HP, long ACK, long HP2, long ACK2) {
    /* 杰尼龟处于死亡复活的死循环中,不可能发动攻击 */
    if (HP2 <= ACK) {
        return -1;
    }
    long result = 1;
    /* 第一次攻击 */
    HP -= ACK2;
    if (HP <= 0) {  /* 皮卡丘被杰尼龟瞬秒,直接返回 1 */
        return result;
    } else if ((HP2 - ACK) <= ACK) {  /* 杰尼龟处于死亡复活的死循环中,直接返回 -1 */
        return -1;
    }
    /* 几个回合是一个循环 */
    long countOfCycle = (HP2 - 1) / ACK;
    /* 每个循环可以贡献的攻击力 */
    long ackOfCycle = (countOfCycle - 1) * ACK2;
    /* 一共需要几轮 */
    long basicCount = HP / ackOfCycle;
    result += basicCount * countOfCycle;
    /* 这几轮攻击之后,HP 还剩下多少 */
    long leftHP = HP % ackOfCycle;
    if (leftHP == 0) {
        /* 如果 leftHP 为 0,说明正好够用,并且最后一次不需要复活 */
        result -= 1;
    } else {
        /* 如果 leftHP 不为 0,说明还有血,但不需要在进行完整的循环 */
        result += leftHP / ACK2 + (leftHP % ACK2 == 0 ? 0 : 1);
    }
    return result;
}
发表于 2020-07-04 17:37:53 回复(0)
/*
代码比较复杂,逻辑如下:
1. 如果被一击毙命,返回-1
2. 如果被两击毙命,且不能对对手一击毙命,返回-1
3. 除了以上情况,如果可以对对手一击毙命,返回1
4. 除了以上情况,分别计算一下,需要几回合攻击可以让对手血量为0,以及每多少回合需要有一回合吃药,
正确处理取余时的问题即可。
*/
class Solution {
public:
    /**
     * 
     * @param HP long长整型 HP
     * @param ACK long长整型 ACK
     * @param HP2 long长整型 HP2
     * @param ACK2 long长整型 ACK2
     * @return long长整型
     */
    long long Pokemonfight(long long HP, long long ACK, long long HP2, long long ACK2) {
        // write code here
        long long res1, res2, res3;
        if(ACK >= HP2 || 2*ACK >= HP2 && ACK2 < HP) {
            return -1;
        }
        else if(ACK2 >= HP) {
            return 1;
        }
        else {
            res1 = HP/ACK2;
            if(HP%ACK2 != 0) {
                res1 = res1 + 1;
            }
            res2 = (HP2-ACK)/ACK;
            if(HP2%ACK == 0) {
                res2 = res2 - 1;
            }
            
            if(res1 < res2 + 1) {
                res3 = 0;
            }
            else {
                res3 = (res1 - res2 - 1)/res2;
                if((res1 - res2 - 1)%res2 != 0) {
                    res3 = res3 + 1;
                }
            }

            return res1 + res3;
        }
    }
};

发表于 2020-03-16 16:31:51 回复(1)
    public long Pokemonfight (long HP, long ACK, long HP2, long ACK2) {
        // write code here
        // 预备知识:a / b 取上界可以用 (a - 1)/ b + 1
        // 需要攻击次数 ackTimes,取上界
        long ackTimes = (HP - 1) / ACK2 + 1;
        // 每经过 recover 轮需要喝一个大药
        long recover = (HP2 - 1) / ACK - 1;
        // ackTimes <= recover + 1 直接打晕皮卡丘;因为最后一次不喝药可以多打一次
        if (ackTimes <= recover + 1) return ackTimes;
        // recover < 0 会被直接打死,recover == 0 每轮都在喝药,都返回 -1
        if (recover <= 0) return -1;
        // 返回 ackTimes + 喝药次数(取 ackTimes - (recover + 1) / recover 上界)
        return ackTimes + (ackTimes - recover - 2) / recover + 1;
    }
代码比较简单的,写出来花了好久,脑子真是不够用
发表于 2020-08-31 17:46:22 回复(0)
class Solution {
public:
    /**
     * 
     * @param HP long长整型 HP
     * @param ACK long长整型 ACK
     * @param HP2 long长整型 HP2
     * @param ACK2 long长整型 ACK2
     * @return long长整型
     */
    long long Pokemonfight(long long HP, long long ACK, long long HP2, long long ACK2) {
        if(ACK >= HP2)
            return -1;
        if(ACK2 >= HP)
            return 1;
        if(HP2 <= 2*ACK)
            return -1;
        return ceil((ceil((double)ACK/ACK2*HP)+1-HP2)/(HP2-ACK-HP2%ACK)) + ceil((double)HP/ACK2);
    }
};

发表于 2020-07-10 21:33:17 回复(0)
4行代码的解法(贪心法)
杰尼龟残血的那个回合必剩余HP2%ACK血量,龟必加满血到HP2,然后皮卡丘必攻击龟一次,合起来龟净增加HP2-ACK-HP2%ACK血量,皮卡丘血量不变。
因此不妨把杰尼龟血量上限变成无限大,透支残血回合,直接加到能和皮卡丘一来一去耗死为止即可(另外注意杰尼龟要多加1点血,防止杰尼龟先死,比如8 3 8 1这个用例,杰尼龟有25点血才能耗死皮卡丘,否则会先死,因为皮卡丘先攻击)。
代码开头还要做3个判断:1. 杰尼龟是否一下就背皮卡丘秒了 2. 杰尼龟抗住一下后,是否能直接反杀皮卡丘. 3. 如果皮卡丘只须攻击两次就可以秒杰尼龟,那么杰尼龟就必须一直加血(简单模拟一下就可以知道这个),而皮卡丘一直血量不变,游戏无法结束,返回-1;
class Solution {
public:
    /**
     * 
     * @param HP long长整型 HP
     * @param ACK long长整型 ACK
     * @param HP2 long长整型 HP2
     * @param ACK2 long长整型 ACK2
     * @return long长整型
     */
    long long Pokemonfight(long long HP, long long ACK, long long HP2, long long ACK2) {
        // write code here
        if(HP2 <= ACK) return -1;
        if(HP <= ACK2) return 1;
        if(HP2 <= 2*ACK) return -1;
        return ceil((ceil(((double)ACK/ACK2)*HP)+1-HP2)/(HP2 - ACK - HP2%ACK))+ceil(((double)HP/ACK2));
    }
};



发表于 2020-03-11 23:16:06 回复(0)
详见注释
改了好多遍,太难了我
import java.util.*;


public class Solution {

    public long Pokemonfight (long HP, long ACK, long HP2, long ACK2) {
        if(ACK >= HP2){    //第一回合,先被打,一下被K.O.,打不过
            return -1;     
        }
        if(ACK2 >= HP){    //苟过第一下就能立马反杀,能打过,一局结束
            return 1;      
        }
        if(HP2 - ACK <= ACK){  //回血后没机会打,一直只能回血,打不过
            return -1;
        }
        long n = HP%ACK2==0? HP/ACK2 : HP/ACK2+1;   //皮卡丘被攻击总次数
        long m = HP2%ACK==0? HP2/ACK-1 : HP2/ACK;   //第一次回血前的攻击次数
        long drug = (n-1)%(m-1)==0?(n-1)/(m-1) - 1 : (n-1)/(m-1);  //回血次数
        return  n + drug;
    }
}


编辑于 2021-04-10 01:03:39 回复(0)
import java.util.*;
public class Solution {
    /**不能赢的情况是:如果在杰尼龟死之后,皮卡丘ACK大于它生命值的一半,
        *就会陷入加血的循环,没法打
     *  总思路就是,如果能赢,答案=打的回合数+加血的回合数
     * @param HP long长整型 HP
     * @param ACK long长整型 ACK
     * @param HP2 long长整型 HP2
     * @param ACK2 long长整型 ACK2
     * @return long长整型
     */
    public long Pokemonfight (long HP, long ACK, long HP2, long ACK2) {
        // write code here
        
        long ans=(long)(Math.ceil(HP*1.0/ACK2));//打的次数
        if( ans *ACK <HP2) return ans;//无需加血,就能赢
        if(ACK >= HP2/2.0) return -1;//打不过
        long dif=ans*ACK-HP2;
        if(dif==0) return ans+1;//刚刚加一次
        long youxiao=HP2/ACK-1;
        if(HP2%ACK==0) 
        youxiao-=1;
        ans+=(long)(Math.ceil(dif*1.0/(youxiao*ACK)));//打很多次
     return ans;
    }
}
发表于 2020-10-12 20:30:29 回复(0)
试了好多次才试对,
import math
class Solution:
    def Pokemonfight(self , HP , ACK , HP2 , ACK2 ):
        num1=0
        num2=0
        num1=math.ceil(HP/ACK2)
        if HP2%ACK==0:
            num2=int(HP2/ACK)-1
        else:
            num2=int(HP2/ACK)
        if num1<=num2:
            return num1
        elif HP2<=2*ACK:
            return -1
        else:
            return num1+math.ceil((num1-num2)/(num2-1))

发表于 2020-10-06 00:05:12 回复(0)
function Pokemonfight( HP ,  ACK ,  HP2 ,  ACK2 ) {
    if(ACK >= HP2 || (ACK2 < HP && ACK*2 >= HP2)) return -1;
    // 皮卡丘被一次打死
    if(HP <= ACK2)  return 1;
    let hp2 = HP2;
    let hp = HP;
    // 打死皮卡丘需要多少轮?HP/ACK2+1
    let count = HP%ACK2 === 0 ? (HP/ACK2):(HP/ACK2)+1;
    // 不吃药能撑几轮?HP2/ACK --- 每打几轮需要吃药?
    let noEat = HP2%ACK === 0 ? (HP2/ACK)-1:(HP2/ACK);
    if(count < noEat){
        count = parseInt(count);
        return count;
    }else {
        // 打死皮卡丘的过程中需要吃多少次药?---吃药过程不能打---而且会被攻击---血量减少一次攻击机会
        var eat = parseInt(count-noEat)/parseInt(noEat-1)+1;
        return parseInt(eat+count);
    }
}

发表于 2020-08-26 15:55:35 回复(0)
import math
class Solution:
    def Pokemonfight(self , HP , ACK , HP2 , ACK2 ):
        # write code here
        #打不过
        if ACK>=HP2:
            return -1
        if 2*ACK>=HP2 and ACK2<HP:
            return -1
        #站撸能过
        if (math.ceil(HP2/ACK)-1)*ACK2>=HP:
            return math.ceil(HP/ACK2)
        #回血锤它
        damage_f=(math.ceil(HP2/ACK)-2)*ACK2
        if HP%damage_f==0:
            return (HP/damage_f-1)*(math.ceil(HP2/ACK)-1)+(HP%damage_f)/ACK2
        else:
            return int(HP/damage_f)*(math.ceil(HP2/ACK)-1)+(HP%damage_f)/ACK2

发表于 2020-07-07 14:52:34 回复(0)
难道只有我一个人认为满血复活是死了后在补血复活么...
发表于 2020-03-29 11:00:07 回复(4)