守望者的逃离——贪心

【NOIP 2007普及】守望者的逃离

Description

恶魔猎手尤迪安野心勃勃,他背叛了暗夜精灵,率领深藏在海底的娜迦族企图叛变。守望者在与尤迪安的交锋中遭遇了围杀,被困在一个荒芜的大岛上。为了杀死守望者,尤迪安开始对这个荒岛施咒,这座岛很快就会沉下去。到那时,岛上的所有人都会遇难。守望者的跑步速度为17m/s,以这样的速度是无法逃离荒岛的。庆幸的是守望者拥有闪烁法术,可在1s内移动60m,不过每次使用闪烁法术都会消耗魔法值10点。守望者的魔法值恢复的速度为4点/s,只有处在原地休息状态时才能恢复。

现在已知守望者的魔法初值M,他所在的初始位置与岛的出口之间的距离S,岛沉没的时间T。你的任务写写一个程序帮助守望者计算如何在最短的时间内逃离荒岛,若不能逃出,则输出守望者在剩下的时间能走的最远距离。注意:守望者跑步、闪烁或休息活动均以秒(s)为单位,且每次活动的持续时间为整数秒。距离的单位为米(m)。

Input  Format

在输入文件escape.in仅一行,包括空格隔开的三个非负整数M,S,T。

Output Format

在输出文件escape.out包括两行:

第1行为字符串“Yes”或“No”(区分大小写),即守望者是否能逃离荒岛。

第2行包含一个整数。第一行为“Yes”(区分大小写)时表示守望者逃离荒岛的最短时间;

第一行为“No”(区分大小写)时表示守望者能走的最远距离。

Sample Input

【输入样例1】

39 200 4

【输入样例2】

36 255 10

Sample Output

【输出样例1】

No

197

【输出样例2】

Yes

6

Time Limitation

1s

 

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
1 #include < stdio.h >
2 #include < stdlib.h >
3   int M,S,T,max = 0 ,min = 30000000 ;
4 FILE * in , * out ;
5 //max存储可以走的最长距离,min存储最短时间
6 void dfs( int dis, int t, int m)
7 { //dis表示已经用了t秒走的距离,m表示剩下的魔法点数
8 int i,j = 0 ,flag = 0 ,x,y; //flag用来控制下面的一个循环
9 if (t <= T){
10 if (dis > max && dis <= S)max = dis; //max存储不大于T的最大距离
11 if (dis >= S && t < min)min = t; //如果已走距离已经超过T,在min内存储最短时间
12 if (m >= 10 ) //点数够闪烁一次,就闪烁
13 dfs(dis + 60 ,t + 1 ,m - 10 );
14
15 else
16 while (flag == 0 )
17 {
18 j ++ ;
19 for (i = 1 ;i <= T - t;i ++ ) //补法,知道足够放j次闪烁
20 if (m + i * 4 >= j * 10 )
21 break ;
22
23 if (t + i + j <= T)
24 {
25 if ( 17 * (i + j) < 60 * j) //用来判断用跑i+j的距离和闪烁j次的距离哪个更长
26 {
27 if(dis+60*j> S) //这一步在下面解释
28 {
29 x=(S-dis)/17;y=(S-dis)%17 ;
30 if(y!=0)x++ ;
31 if(min>t+x)min=t+ x;
32 }
33 else
34 {
35 flag = 1 ;dfs(dis + 60 * j,t + i + j,m + i * 4 - j * 10 );
36 }
37 }
38 }
39 else //如果用法太耗时,那么就跑步
40 {
41 flag = 1 ;dfs(dis + 17 ,t + 1 ,m);
42 }
43 }
44 }
45 }
46
47 int main()
48 {
49 in = fopen( " escape4.in " , " r " );
50 out = fopen( " escape.out " , " w " );
51 fscanf( in , " %d%d%d " , & M, & S, & T);
52 dfs( 0 , 0 ,M);
53 if (min != 30000000 )
54 fprintf( out , " Yes\n%d\n " ,min);
55 else
56 fprintf( out , " No\n%d\n " ,max);
57 fclose( in );fclose(out);
58 return 0;
59 }

红色代码区程序的必要性在于:假如还剩下17m的距离,6点的魔法,还可以用的时间是2s。按照程序的运行 ,必然是

补齐10点法,花1s,闪烁一次,一共两秒。但实际只要1s就能走到。所以这一代码区是为了保证min中存的是走出小岛

用的最少时间。

程序思想:每一次走有两种选择,闪烁或者跑步。如果法足够,当然用闪烁。如果不够,则补法,然后衡量补法时间加上

闪烁的时间内用跑步走的距离长,还是闪烁走的距离长,选其中更长的方法走。因为闪烁一次要比跑步1s走的距离长,所

以我还是要尽量闪烁来走路。因而如果补法加闪烁1次没有跑步走的距离长,那么我继续补法,闪2次,如果比跑步的距离

长,则不继续补,选择闪烁2次,如果还没有跑步距离长,那继续补,闪3次...这也就是代码中while()循环的必要性。如

果已经用了的时间加上补法和闪烁的时间比限制时间大,那么不继续补法,选择跑步。

例如:现有0点魔法,那么补法加闪烁的时间为3+1=4s,将走60m,而我直接跑,会走17*4=68m的距离,此时继续补

法,不到可以闪烁两次时补法时间加闪烁的时间为5+1=6s,将走120m,而直接跑会走17*6=102m。此时如果剩下的时

间不小于6s,显然我选择补法然后走120m(当然还有上面讲到的红色代码区的必要判断)。如果没有6s,那么选择跑步。

 

总结:这个程序改变了我对深搜效率低的一贯看法,有好的剪枝,深搜还是很好用的。这道题用动态规划就难多了啊。

转载于:https://www.cnblogs.com/aiyite826/archive/2010/08/19/1803413.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值