恶魔猎手尤迪安野心勃勃,他背叛了暗夜精灵,率领深藏在海底的娜迦族企图叛变。守望者
在与尤迪安的交锋中遭遇了围杀,被困在一个荒芜的大岛上。为了杀死守望者,尤迪安开始对这
个荒岛施咒,这座岛很快就会沉下去。到那时,岛上的所有人都会遇难。守望者的跑步速度为
17m/s,以这样的速度是无法逃离荒岛的。庆幸的是守望者拥有闪烁法术,可在1s内移动60m,不
过每次使用闪烁法术都会消耗魔法值10点。守望者的魔法值恢复的速度为4点/s,只有处在原地
休息状态时才能恢复。
现在已知守望者的魔法初值M,他所在的初始位置与岛的出口之间的距离S,岛沉没的时间T。
你的任务写写一个程序帮助守望者计算如何在最短的时间内逃离荒岛,若不能逃出,则输出守望
者在剩下的时间能走的最远距离。注意:守望者跑步、闪烁或休息活动均以秒(s)为单位,且每
次活动的持续时间为整数秒。距离的单位为米(m)。
这几天正在为ACM市赛作准备,突发奇想回到了Vijos刷这道初中就留下了深刻记忆的题目…
好几番挣扎之后终于AC了,普及组的水平对于大二的我来说可能仍然不容易吧
package com.company;
import java.util.Scanner;
public class Main {
public static int specialDevide(int num1,int num2) {
if (num1 % num2 == 0){
return num1 / num2;
}else{
return num1 / num2 + 1;
}
}
public static void print(String s,int num){
System.out.println(s + "\n" +num);
System.exit(0);
}
public static boolean canBlinkTwice(int timeLimit,int nowMagic,int distance){
int t = specialDevide(20 - nowMagic , 4);
int t2 = specialDevide(distance, 60);
if(t + 2 <= timeLimit && t2 >= 2){
return true;
}else{
return false;
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int magic = sc.nextInt();
int distance = sc.nextInt();
int timeLimit = sc.nextInt();
int t = timeLimit;//备份源数据
int dis = distance;//备份源数据
//阶段1:连续闪烁
int n = magic / 10;
int d = n * 60;
//情况1.1:阶段1中间,时间用尽,而且没有逃出
if (timeLimit < n && d < distance){
print("No", timeLimit * 60);
}
//情况1.2:阶段1中间,已经逃出了——>(时间用尽,不过已经逃出了)||(时间未用尽,不过已经逃出了)
if ((t * 60 >= distance && timeLimit <= n) || (d >= distance && timeLimit >= n)){
print("Yes", specialDevide(distance, 60));
}
//情况1.3:阶段1中间,时间未用尽
magic -= n * 10;
timeLimit -= n;
distance -= d;
//阶段2:连续休息+闪烁,最后跑步
for (;;){
if (distance <= 0) print("Yes",t - timeLimit);//时间未到,已经逃脱
if (timeLimit == 0) print("No",dis - distance);//时间已到,没有逃脱
int recoverTime = specialDevide(10 - magic, 4);
//System.out.print("魔法:" + magic + "距离:" + distance + "时间:" + timeLimit);
if (magic >= 6 && distance >= 34 && timeLimit >= 1){//休息1秒再闪烁
//System.out.println("休息1秒再闪烁");
timeLimit -= 2;
distance -= 60;
magic = magic + 4 - 10;
continue;
}
if (magic >= 2 && distance >= 51 && timeLimit >= 2){//休息2秒再闪烁
//System.out.println("休息2秒再闪烁");
timeLimit -= 3;
distance -= 60;
magic = magic + 8 - 10;
continue;
}
if (timeLimit >= 7 && distance >= 120){
//System.out.println("闪烁闪烁");
timeLimit -= 7;
distance -= 120;
continue;
}
//System.out.println("直接跑路");
timeLimit -= 1;
distance -= 17;
}
}
}
用递归写的,没有考虑好情况4,数据大了会爆栈也是个大问题
import java.util.Scanner;
public class Main {
public static int getTime(int magic,int distance,int timeLimit){
//已经逃离出去了
if (distance < 0){
return 0;
}
//魔法有余
if (magic >= 10 && timeLimit >= 1) {
//System.out.println("直接闪烁");
return 1 + getTime(magic - 10,distance - 60,timeLimit - 1);
}
//魔法不足但是时间足够,而且闪烁比跑步划算
int t = timeToRegerateMagic(magic);
if (timeLimit >= t + 1 && t + 1 <= timeToRunToEnd(distance)) {
//System.out.println("先休息" + t + "秒,再闪烁");
return t + 1 + getTime(magic + t * 4 - 10,distance - 60,timeLimit - t - 1);
}
//时间不够闪烁了
if (timeLimit >= 1) {
//System.out.println("跑步");
return 1 + getTime(magic,distance - 17,timeLimit - 1);
}
//时间不够1s
System.out.println("No\n" + (d - distance));
System.exit(0);
return -99999;
}
public static int d;
public static int t;
public static int timeToRegerateMagic(int magic){
if ((10 - magic) % 4 == 0){
return (10 - magic) / 4;
}else{
return (10 - magic) / 4 + 1;
}
}
public static int timeToRunToEnd(int distance){
if (distance % 17 == 0){
return distance / 17;
}else{
return distance / 17 + 1;
}
}
public static void main(String[] args) {
// write your code here
Scanner sc = new Scanner(System.in);
int magic = sc.nextInt();
int distance = sc.nextInt();
int timeLimit = sc.nextInt();
d = distance;
t = timeLimit;
System.out.println("Yes\n" + getTime(magic, distance, timeLimit));
}
}
当时错了4个数据点但不知道错哪了,无奈之下从网上苦苦找到了Vijos的测试数据
看了测试数据后…好吧,没办法了只能重构咯
第二次代码改进了结构,对于情况4也有了一定的考虑,考虑的很细但是仍然无法完美解决问题
最优的时候也错了2个数据点
import java.util.Scanner;
public class Main {
public static int specialDevide(int num1,int num2) {
if (num1 % num2 == 0){
return num1 / num2;
}else{
return num1 / num2 + 1;
}
}
public static void print(String s,int num){
System.out.println(s + "\n" +num);
System.exit(0);
}
public static boolean canBlinkTwice(int timeLimit,int nowMagic,int distance){
int t = specialDevide(20 - nowMagic , 4);
int t2 = specialDevide(distance, 60);
if(t + 2 <= timeLimit && t2 >= 2){
return true;
}else{
return false;
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int magic = sc.nextInt();
int distance = sc.nextInt();
int timeLimit = sc.nextInt();
int t = timeLimit;//备份源数据
int dis = distance;//备份源数据
//阶段1:连续闪烁
int n = magic / 10;
int d = n * 60;
//情况1.1:阶段1中间,时间用尽,而且没有逃出
if (timeLimit < n && d < distance){
print("No", timeLimit * 60);
}
//情况1.2:阶段1中间,已经逃出了——>(时间用尽,不过已经逃出了)||(时间未用尽,不过已经逃出了)
if ((t * 60 >= distance && timeLimit <= n) || (d >= distance && timeLimit >= n)){
print("Yes", specialDevide(distance, 60));
}
//情况1.3:阶段1中间,时间未用尽
magic -= n * 10;
timeLimit -= n;
distance -= d;
//阶段2:连续休息+闪烁,最后跑步
for (;;){
int recoverTime = specialDevide(10 - magic, 4);
//作出判断,这次是闪烁还是跑步
//何时应该闪烁?
//第一,只要不是最后一次闪烁选择,而且比跑步划算,就可以闪烁
//第二,如果是最后一次闪烁,此时魔法量>=2,而且比跑步划算,而且时间充裕,就可以闪烁
if (canBlinkTwice(timeLimit,magic,distance) || ( !canBlinkTwice(timeLimit,magic,distance) && (magic >= 2) && (timeLimit > recoverTime + 1) ) ){//闪烁比较划算
//System.out.println("还剩" + magic +"点魔法,休息" + recoverTime + "秒再闪烁");
timeLimit -= recoverTime + 1;
magic = magic + recoverTime * 4 - 10;
distance = distance - 60;
}else{//直接跑路比较划算
//System.out.println("直接跑路");
timeLimit -= 1;
distance = distance -17;
}
if (distance <= 0) print("Yes",t - timeLimit);//时间未到,已经逃脱
if (timeLimit == 0) print("No",dis - distance);//时间已到,没有逃脱
}
}
}
当时感觉魔法过剩的问题太麻烦了,完全没法改(实质是没有思考清楚)
感觉自己已经无力解决,无奈之下百度参考了下别人的题解,遂思路清晰迅速AC掉了…
这道题大概是我做的第一道能算贪心的题吧,感觉对自己来说还是很难的,所以写了一下解题报告…
如果放在ACM的规则下那考试时肯定是没办法AC的…
通过这道题了解到了什么样的题算贪心,以及…充分训练了debug断点调试能力…