题意分析
看到题DP状态还是十分好设计的。
dp[i][j]
d
p
[
i
]
[
j
]
表示当前时刻
i
i
,剩余魔法 所能逃离的最大距离。
状态转移方程为:
dp[i][j]=max(dp[i−1][j−4],dp[i−1][j+10]+60,dp[i−1][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,利用第一个数组转移;第二种就是走,利用第二个数组直接转移。综上,状态转移方程如下:
代码总览
#include<bits/stdc++.h>
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;
}