vijos1431[noip2007]守望者的逃离(背包动规)

描述

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

你的任务写写一个程序帮助守望者计算如何在最短的时间内逃离荒岛,若不能逃出,则输出守望
者在剩下的时间能走的最远距离。注意:守望者跑步、闪烁或休息活动均以秒(s)为单位,且每
次活动的持续时间为整数秒。距离的单位为米(m)。

格式

输入格式

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

输出格式

在输出文件escape.out包括两行:
第1行为字符串“Yes”或“No”(区分大小写),即守望者是否能逃离荒岛。
第2行包含一个整数。第一行为“Yes”(区分大小写)时表示守望者逃离荒岛的最短时间;
第一行为“No”(区分大小写)时表示守望者能走的最远距离。

样例1

样例输入1[复制]

39 200 4

样例输出1[复制]

No
197

样例2

样例输入2[复制]

36 255 10

样例输出2[复制]

Yes
6

限制

1s

提示

30%的数据满足:1<=T<=10,1<=S<=100
50%的数据满足:1<=T<=1000,1<=S<=10000
100%的数据满足:1<=T<=300000,0<=M<=1000,1<=S<=10^8

来源

NOIP2007普及组第3题

Accepted

 
100
0 7556
P1431 守望者的逃离
ksq2013 C++ 2016-08-16 23:04:32

思路1:暴搜

dfs(t,s,m)
    if s>=S then write(yes) AND exit(0)
    if t>T then record(s)
    if m>=10 then dfs(t+1,s+60,m-10)
    else dfs(t+1,s,m+4)
    dfs(t+1,s+17,m)
思路2:没有正确性证明的贪心

greedy()
    for i->1 to T
        if M>=10 then s->s+60 AND M->M-10
        else
            if ((10-M)/4+1)*17>60 then s->s+17
            else i->i+4 AND M->(10-M)/4*4-10+M AND s->s+60
思路3:背包动规

晕倒。一开始思路被纯粹的背包思路和背包写法给圈死了,记忆化搜索||递推想出的solution中的状态基本都处理不好后效性问题,

例如:将暴搜的顺序思路进行到底,得出f[t][m]=max{f[t-1][m+4],f[t-1][m-10]+60,f[t-1][m]+17}

几小时的苦思冥想最后看了标程。

其实我最初的思路已经有了一定的框架,只不过没有跳出自己划的“圈圈”

正解:观察题目,试举出几个例子,可以发现,同一初始魔法值的情况下,任何时刻都有属于它本身独立的最优解,“最优解”即最远距离咯。

而当前若选择行走,得到的总路程仅与与上一时刻的最优解有关;

    当前若选择用魔法,得到的总路程仅与上一时刻的魔法值和上一时刻用魔法走出的距离有关系(因为对于之后的某些特定时刻,不断停下蓄魔法槽比不断行走更优,因为不管怎样,最贪心的想法就是用完魔法值后不断行走)

所以,我们得出了解决方案:

f1[i]=f1[i-1]+60 AND M-=10    or    f1[i]=f1[i-1] AND M+=4

f2[i]=max{f1[i],f2[i-1]+17}

程序如下:

Accepted

 
100
0 7556
P1431 守望者的逃离
ksq2013 C++ 2016-08-16 23:04:32

#include
#include
using namespace std;
typedef long long ll;
inline ll mx(ll x,ll y)
{
    if(x>y)return x;
    return y;
}
ll M,S,T;
struct state{ll dis,magic,ans;}f[300001];
int main()
{
    scanf("%lld%lld%lld",&M,&S,&T);
    f[0].dis=0,f[0].magic=M,f[0].ans=0;
    for(int i=1;i<=T;i++){
        f[i].magic=f[i].dis=f[i].ans=0;
        if(f[i-1].magic>=10){
            f[i].magic=f[i-1].magic-10;
            f[i].dis=f[i-1].dis+60;
        }
        else{
            f[i].magic=f[i-1].magic+4;
            f[i].dis=f[i-1].dis;
        }
        f[i].ans=mx(f[i-1].ans+17,f[i].dis);
        if(f[i].ans>=S){
            printf("Yes\n%d\n",i);
            return 0;
        }
    }
    printf("No\n%lld\n",f[T].ans);
    return 0;
}

你可能感兴趣的:(动态规划,背包动规)