Vijos 1431 守望者的逃离(贪心+DP)

题意分析

看到题DP状态还是十分好设计的。
dp[i][j] d p [ i ] [ j ] 表示当前时刻 i i ,剩余魔法 j j 所能逃离的最大距离。
状态转移方程为:
dp[i][j]=max(dp[i1][j4],dp[i1][j+10]+60,dp[i1][j]+17) d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j − 4 ] , d p [ i − 1 ] [ j + 10 ] + 60 , d p [ i − 1 ] [ j ] + 17 )
但是看到数据范围,就瞬间明白GG了,因为数据范围很大,数组开不下。

所以考虑优化办法。
我最先想到的办法是滚动数组,滚动数组的话,会超时。
其实经过计算,就会发现,如果魔法足够的情况,最优的策略是不断Bilnk,没有魔法之后就休息,有了足够魔法接着Blink,但是这样就会有一个问题。 到最后如果时间不够了,其实走是直接能走出去的,然而却在休息,无法计算出正确答案。当然解决办法就是详细的分类讨论。

如果不想分类讨论,那么就要设计成DP。
根据贪心的思想,能Blink就Blink,这样的话,可以用一个数组来表示当前Blink走的最大距离。
也就是说用这个数组来维护Blink距离和魔法值的关系。
然后用另一个数组来表示Blink和走的关系,也就是完成状态的转移。
接着考虑,对于第 i i 秒,决策无非两种:第一种就是Blink,利用第一个数组转移;第二种就是走,利用第二个数组直接转移。综上,状态转移方程如下:
dp[i]=max(dp[i1]+17,magicdis[i]) d p [ i ] = m a x ( d p [ i − 1 ] + 17 , m a g i c d i s [ i ] )

代码总览

#include
using namespace std;
const int nmax = 300000 + 5;
int dp[nmax];
int magicdis[nmax];
int Ma;
int M,S,T;
int main(){
    scanf("%d %d %d",&M,&S,&T);
    bool isleave = false;
    int ans = -1;
    for(int i = 1;i<=T;++i){
        if(M>=10){
            magicdis[i] = magicdis[i-1] + 60;
            M-=10;
        }else{
            magicdis[i] = magicdis[i-1];
            M += 4;
        }
        dp[i] = max(magicdis[i],dp[i-1] + 17);
        if(dp[i] >= S){
            isleave = true;
            ans = i;
            break;
        }
    }
    if(isleave){
        printf("Yes\n");
        printf("%d\n",ans);
    }else{
        printf("No\n");
        printf("%d\n",dp[T]);
    }
    return 0;
}

你可能感兴趣的:(算法---动态规划,算法---贪心策略)