【问题描述】
恶魔猎手尤迪安野心勃勃.他背叛了暗夜精灵,率深藏在海底的那加企图叛变:守望者在与尤迪安的交锋中遭遇了围杀.被困在一个荒芜的大岛上。为了杀死守望者,尤迪安开始对这个荒岛施咒,这座岛很快就会沉下去,到那时,岛上的所有人都会遇难:守望者的跑步速度,为17m/s,以这样的速度是无法逃离荒岛的。庆幸的是守望者拥有闪烁法术,可在1s内移动60m,不过每次使用闪烁法术都会消耗魔法值10点。守望者的魔法值恢复的速度为4点/s,只有处在原地休息状态时才能恢复。
现在已知守望者的魔法初值M,他所在的初始位置与岛的出口之间的距离S,岛沉没的时间T。你的任务是写一个程序帮助守望者计算如何在最短的时间内逃离荒岛,若不能逃出,则输出守望者在剩下的时间内能走的最远距离。注意:守望者跑步、闪烁或休息活动均以秒(s)为单位。且每次活动的持续时间为整数秒。距离的单位为米(m)。
实现以下接口:
int helpWatcherEscape(int uiMagic, int uiDistance,
int uiSec,StringBuilder pRstOut)
功能:判断守望者是否能够逃出荒岛
输入参数:
int uiMagic:无符号整型,守望者的初始魔法值
int uiDistance:无符号整型,守望者所在的初始位置与岛出口之间的距离。
int uiSec:无符号整型,岛沉没需要的时间,单位为秒
输出参数(指针指向的内存区域保证有效):
StringBuilder pRstOut:输出守望者能否逃出荒岛,若能逃出输出“Yes”,不能输出“No”,注意大小写。
返回值:
int : 若守望者能逃出荒岛,输出逃出荒岛所用的最短时间,不能逃出则输出守望者能逃出的最大距离。
【解答】
主体思想:先狂用闪烁,魔法不够了再根据时间、魔法值和距离来判断下一步是跑步还是恢复魔法值后闪烁。
思考后得出了一个用光魔法后由时间、魔法、距离的关系所做的决策。
由上表格,就可以使用范围判断法来确定决策了。
解释:例如,当魔法值是0或1时,如要闪烁一次需恢复3秒,加上闪烁时间共4秒,才跑60m,而跑步就可以跑17×4=68m了。因此,如在时间足够的前提下,恢复5秒闪烁2次,共花费7秒,跑了60×2=120m,而跑步只能跑17×7=119m。因此,在条件允许的情况下,所作决策为恢复+闪烁。至于距离剩余的条件,大家可以想一下,当前的剩余距离如果并不需要使用恢复+闪烁,而可以用更少的时间用跑步来完成,那当然用跑步好啦!当剩余距离不大于102时,跑步最多只需花102÷17=6(秒),而不需要再用7秒了。
魔法值剩余2~9的决策和条件原因同上。
此题用贪心需要考虑的一些点
1. 当开始狂用闪烁时如果魔法值还足够可是已经跑完了就会错误。
2. 同样,狂用闪烁时如果时间不够闪烁了也会错误。
3. 上面讲过了,剩余距离无需恢复+闪烁只需跑步的情况也要考虑到。
考虑到以上几点就可以开始写代码了。
【Java实现】
public class WatchmenEscape { public static void main(String[] args) { helpWatcherEscape(39, 200, 4); // 预期输出:"No 197" helpWatcherEscape(36, 255, 10); // 预期输出:"Yea 6" } public static void helpWatcherEscape(int uiMagic, int uiDistance, int uiSec) { int SpaceElse = 0, TimeleftElse = uiSec; // SpaceElse为行动距离累加,TimeleftElse为开始剩余时间另存 while (uiMagic >= 10 && uiSec > 0 && uiDistance - SpaceElse > 0) { // 开始闪烁使用模拟 if (uiDistance - SpaceElse > 17) { SpaceElse += 60; uiMagic -= 10; uiSec--; } else { System.out.println("Yes" + (TimeleftElse - uiSec + 1)); } } while (uiSec > 0 && uiDistance - SpaceElse > 0) { // 决策判断 if ((uiMagic == 0 || uiMagic == 1) && uiSec >= 7 && uiDistance - SpaceElse > 102) // 见表格 { uiSec -= 7; SpaceElse += 120; } else if (uiMagic >= 2 && uiMagic <= 5 && uiSec >= 3 && uiDistance - SpaceElse > 34)// 见表格 { uiMagic -= 2; uiSec -= 3; SpaceElse += 60; } else if (uiMagic >= 6 && uiSec >= 2 && uiDistance - SpaceElse > 17) // 见表格 { uiMagic -= 6; uiSec -= 2; SpaceElse += 60; } else { SpaceElse += 17; uiSec--; } } if (uiDistance <= SpaceElse) { System.out.println("Yes " + (TimeleftElse - uiSec)); } else { System.out.println("No " + SpaceElse); } } }
【C++实现】
#include<iostream> using namespace std; void escape(int Mana,int Space,int Timeleft) //逃离决策模拟函数 { int SpaceElse=0,TimeleftElse=Timeleft; //SpaceElse为行动距离累加,TimeleftElse为开始剩余时间另存 while(Mana>=10 && Timeleft>0 && Space-SpaceElse>0){ //开始闪烁使用模拟 if(Space-SpaceElse>17){ SpaceElse+=60; Mana-=10; Timeleft--; } else cout<<"Yes "<<TimeleftElse-Timeleft+1; } while(Timeleft>0 && Space-SpaceElse>0){ //决策判断 if((Mana==0 || Mana==1) && Timeleft>=7 && Space-SpaceElse>102) //见表格 { Timeleft-=7;SpaceElse+=120; } else if(Mana>=2 && Mana<=5 && Timeleft>=3 && Space-SpaceElse>34)//见表格 { Mana-=2;Timeleft-=3;SpaceElse+=60; } else if(Mana>=6 && Timeleft>=2 && Space-SpaceElse>17) //见表格 { Mana-=6;Timeleft-=2;SpaceElse+=60; } else { SpaceElse+=17;Timeleft--; } } if(Space<=SpaceElse) cout<<"Yes "<<TimeleftElse-Timeleft; else cout<<"No "<<SpaceElse; return; } int main(void) { int Mana,Space,Timeleft;//Mana为剩余魔法值,Space为距离,Timeleft为剩余时间 cin>>Mana>>Space>>Timeleft;//输入:39 200 4 escape(Mana,Space,Timeleft);//输出:No 197 cin>>Mana>>Space>>Timeleft;//输入:36 255 10 escape(Mana,Space,Timeleft);//输出:Yes 6 return 0; }