题意
在一条路上有草地
(
G
)
(G)
(G)水
(
W
)
(W)
(W)和石头
(
L
)
(L)
(L)。
草地:可以走也可以飞。
水:可以游也可以飞。
石头:只能飞过去。
游:每米需要
3
s
3s
3s,但能得到
1
1
1耐力。
走:每米需要
5
s
5s
5s,但能得到
1
1
1耐力。
飞:每米需要
1
s
1s
1s,且消耗
1
1
1耐力。
然后用 n n n对(类型+距离)数据描述了这段路,问你在耐力不能减为负的情况下从起点走完最小耗时。(保证肯定可以走到)
思路
可以知道:
- 如果飞过石头时耐力不够了,可以在之前来回走得到耐力。
- 如果耐力有的多可以将走或游换成飞。
于是先按照走草地游水飞石头(耐力不够来回走)算出最后剩下的耐力。
在用这些耐力依照最小需要改变一些路段的行动方式。
最小需要则可以通过当前路段到终点的石头和非石头的距离求出。
需要知道的一点是当长度为
l
e
n
len
len的一段路的运动方式改变时,多消耗的耐力是
l
e
n
∗
2
len*2
len∗2。
然后可以改动的最大长度就是
m
i
n
(
n
e
e
d
[
i
]
/
2
,
l
e
n
g
t
h
[
i
]
)
min(need[i]/2,length[i])
min(need[i]/2,length[i])了。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long LL;
const int Mx=1e5+5;
char Map[Mx];
int n;
LL len[Mx],nd[Mx],ans;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lld",&len[i]),len[i]*=2;
scanf("%s",Map+1);
LL ans=0,p=0,lst=1;bool hw=0;
for(int i=1;i<=n;i++){
switch(Map[i]){
case 'G':ans+=len[i]*5,p+=len[i];break;
case 'W':ans+=len[i]*3,p+=len[i],hw=1;break;
case 'L':
if(p>len[i])p-=len[i],ans+=len[i];
else{
lst=i+1;
if(hw)ans+=len[i]+(len[i]-p)*3,p=0;
else ans+=len[i]+(len[i]-p)*5,p=0;
}
}
}
for(int i=n;i>=lst;i--){
if(Map[i]=='L')nd[i-1]=max(len[i],nd[i]+len[i]);
else nd[i-1]=max(0ll,nd[i]-len[i]);
}
LL t=0,tmp=0;
for(int i=lst;i<=n;i++){
if(Map[i]=='L')t-=len[i];
else t+=len[i];
if(Map[i]=='G'){
tmp=min(len[i]*2,t-nd[i]);
if(tmp<=p){
p-=tmp;
ans-=tmp*2;
t-=tmp;
}else{
ans-=p*2;
return printf("%lld",ans/2),0;
}
}
}
ans-=p;
printf("%lld",ans/2);
}