目录
题目:
笔者最开始的思路:
守望者有两种移动方式,按照题意,肯定要选择一种最快的移动方式
选择哪一种?我们可以先允许时间为非整数秒,算一下两种移动方式的速度:
第一种:跑步,17m/s
第二种:如果有足够的能量,不需要休息恢复,就是60m/s,如果需要休息恢复,10点能量需要2.5秒恢复,移动一次60米,需要1秒,平均速度60/(2.5+1)约为17.14m/s。
这说明1、就平均速度来看,用闪烁比跑步快,整一个过程中,非特殊情况应当尽可能都用闪烁。2、17.14m/s和17m/s相差很小,由于题目对时间为整数秒的限定,在能量不足并且临界状态下的个别几次应当选择跑步
何为临界状态?可能是只剩下几秒,如果休息充满能量岛就沉没了,几秒内能跑出去,或是剩余的距离很短,闪烁不如跑步快。
因为整数秒的要求,闪烁的时间计算不能直接用17.14m/s,应当根据能量值(用M表示)分情况讨论
M>=10 :1秒移动60米,速度60m/s
6<=M<=9:用1秒充4个能量,然后用1秒移动60米,速度30m/s
2<=M<=5:用2秒充8个能量,然后用1秒移动60米,速度20m/s
0<=M<=1:用3秒充12个能量,然后用1秒移动60米,速度15m/s
需要注意,0<=M<=1时,速度比17m/s小,但不意味着遇到这种情况都采用跑步,因为充能并用一次闪烁M会变成2或3,计算这两个过程的速度,为7秒移动120米,速度17.14m/s,还是比17m/s快的。除非是临界状态,闪烁完一次就结束了,不需要进行第二次闪烁,这时就适合采用跑步。
举个例子吧,题目的第一个测试用例,能量M=39,距离S=200,时间T=4,用dis表示已走的距离,用t表示已经过的时间
M | S-dis | dis | T-t | t |
39 | 200 | 0 | 4 | 0 |
9 | 20 | 180 | 1 | 3 |
9 | 3 | 197 | 0 | 4 |
结果是NO,最大距离为197米。
题目中的这个样例不够典型,对学生的干扰是很大的(可恶的出题人)
我在debug时自己写了一个例子,M=19,S=264,T=15
编号 | M | S-dis | dis | T-t | t |
1 | 19 | 264 | 0 | 15 | 0 |
2 | 9 | 204 | 60 | 14 | 1 |
3 | 3 | 144 | 120 | 12 | 3 |
4 | 1 | 84 | 180 | 9 | 6 |
5 | 1 | -1 | 265 | 4 | 11 |
1-2,有超过10个能量,优先用闪烁;2-3,用闪烁,2秒移动60米;3-4,用闪烁,3秒移动60米;4-5,注意这里M=1,并且属于临界情况,如果用闪烁,4秒移动60米,最后24米,跑步2秒完成,还需6秒,如果全都用跑步,只需5秒就能完成85米。
结果为YES,最短时间为11秒。
笔者的代码:
第一步,一直用闪烁把能量消耗到小于10
第二步,进入循环,分别对时间是否足够和M的三种情况讨论
#include<stdio.h>
int main()
{
int M, S, T;
while (scanf("%d %d %d", &M, &S, &T) != EOF)
{
int dis = 0;
int t = 0;
dis += (M / 10) * 60;
t += M / 10;
if (dis >= S)//一直用闪烁到逃出的情况
{
if (60 * T < S)//时间太短了,一直闪烁不停都逃不出
{
printf("No\n");
printf("%d\n", 60 * T);
continue;
}
else//一直闪烁可以逃出
{
printf("Yes\n");
printf("%d\n", (S - 1) / 60 + 1);
continue;
}
}
if (t > T)//完成闪烁后未逃出且已超时
{
printf("No\n");
printf("%d\n", T * 60);
continue;
}
M %= 10;
while (1)
{
//时间不够,对M分出3种情况
if (M >= 6 && M <= 9 && T - t <= 1)
{
dis += 17 * (T-t);
if (dis < S)
{
printf("No\n");
printf("%d\n", dis);
break;
}
else
{
printf("Yes\n");
printf("%d\n", T - t);
break;
}
continue;
}
if (M >= 2 && M <= 5 && T - t <= 2)
{
dis += 17 *( T-t);
if (dis < S)
{
printf("No\n");
printf("%d\n", dis+8);
break;
}
else if (dis - 17 >= S)
{
printf("Yes\n");
printf("%d\n", t + 1);
break;
}
else
{
printf("Yes\n");
printf("%d\n", t + 2);
break;
}
continue;
}
if (M >= 0 && M <= 1 && T - t <= 3)
{
dis += 17 * (T-t);
if (dis < S)
{
printf("No\n");
printf("%d\n", dis);
break;
}
else if (dis - 17 * 2 >= S)
{
printf("Yes\n");
printf("%d\n", t );
break;
}
else if (dis - 17 >= S)
{
printf("Yes\n");
printf("%d\n", t+1 );
break;
}
else
{
printf("Yes\n");
printf("%d\n", t +2);
break;
}
continue;
}
//时间够的情况
if (M >= 6 && M <= 9&&T-t>1)
{
for (int ii = 0; ii <= 1; ii++)//判断是否靠近临界情况
{
if (dis + 17 * ii >= S)
{
printf("Yes\n");
printf("%d\n", t + ii);
goto exit;
}
}//非临界情况,使用闪烁
M += 4;
t += 2;
M %= 10;
dis += 60;
if (dis >= S)
{
printf("Yes\n");
printf("%d\n", t);
break;
}
continue;
}
if (M >= 2 && M <= 5&&T-t>2)
{
for (int ii = 0; ii <= 2; ii++)//判断是否靠近临界情况
{
if (dis + 17 * ii >= S)
{
printf("Yes\n");
printf("%d\n", t + ii);
goto exit;
}
}//非临界情况,使用闪烁
M += 8;
t += 3;
M %= 10;
dis += 60;
if (dis >= S)
{
printf("Yes\n");
printf("%d\n", t);
break;
}
continue;
}
if (M >= 0 && M <= 1&&T-t>3)
{
for (int ii = 0; ii <= 7; ii++)//判断是否靠近临界情况
{//这里ii的上界更高,是因为要考虑下一步能否闪烁
if (dis + 17 * ii >= S)
{
printf("Yes\n");
printf("%d\n", t + ii);
goto exit;
}
}
M += 12;
t += 4;
M %= 10;
dis += 60;
if (dis >= S)
{
printf("Yes\n");
printf("%d\n", t);
break;
}
continue;
}
exit:break;
}
}
return 0;
}
是不是很复杂?代码长达179行。
提交这个代码后我一直在反思是不是写复杂了,其他的文章给出此题的代码基本都在50行以内,其实我们可以换一种思路。
以下是参考了大佬的思路后写的第二份代码
更优解:
#include<stdio.h>
int main()
{
int M, S, T;
while (scanf("%d %d %d", &M, &S, &T) != EOF)
{
int t=1,dis1=0,dis2=0;
for(;t<=T&&dis2<S;t++)
{
dis2+=17;
if(M>=10)
{
dis1+=60;
M-=10;
}
else
M+=4;
dis2=dis1>dis2?dis1:dis2;
}
if(dis2>=S)
{
printf("Yes\n");
printf("%d\n",t-1);
}
else
{
printf("No\n");
printf("%d\n",dis2);
}
}
return 0;
}
这种思路只用一个按照时间逐秒递增的for循环,循环到逃出岛屿或时间不足结束
使用dis1和dis2两个变量计算当前时间能前进的最大距离,dis1只记录闪烁的距离,dis2记录跑步的距离,并用dis1更新dis2。
为什么我会把这样简单30行解决的问题写成179行?回想最开始的思路,其实也很简单明了,但写出来以后只能满足少数几个测试用例,当时的我不服气,认为自己的思路是对的,把没通过的情况称为个别的临界情况,需要再加判断条件,找出这些临界情况,并把它们排除,于是我开始了debug。
不知不觉,代码就长了,堆积成山。
建议:如果发现临界情况多,就换一种思路吧,如果没有别的思路,就做下一题吧(特别是考试的时候,如果愿意,平时投入点时间debug也很快乐)。