题意
数轴上有N个地方可以产金币,你首先需要去激活它,然后在T秒后会产出一个金币。每个地方只会产一次。问收集所有N个硬币,并最终走到E的最短时间
题解
考虑dp
定义d[i]表示捡完[1,i]最小花费
转移式
d
[
i
]
=
m
i
n
(
d
[
i
]
,
d
[
j
]
+
a
[
i
]
−
a
[
j
]
+
m
a
x
(
a
[
i
]
−
a
[
j
+
1
]
,
T
−
(
a
[
i
]
−
a
[
j
+
1
]
)
)
+
m
a
x
(
a
[
i
]
−
a
[
j
+
1
]
,
T
−
m
a
x
(
a
[
i
]
−
a
[
j
+
1
]
,
T
−
(
a
[
i
]
−
a
[
j
+
1
]
)
)
)
)
d[i]=min(d[i],d[j]+a[i]-a[j]+max(a[i]-a[j+1],T-(a[i]-a[j+1]))+max(a[i]-a[j+1],T-max(a[i]-a[j+1],T-(a[i]-a[j+1]))))
d[i]=min(d[i],d[j]+a[i]−a[j]+max(a[i]−a[j+1],T−(a[i]−a[j+1]))+max(a[i]−a[j+1],T−max(a[i]−a[j+1],T−(a[i]−a[j+1]))))
好像写得比较复杂
简化一下就是
d
[
i
]
=
d
[
j
]
+
a
[
i
]
−
a
[
j
]
+
m
a
x
(
T
,
2
∗
(
a
[
i
]
−
a
[
j
+
1
]
)
)
d[i]=d[j]+a[i]-a[j]+max(T,2*(a[i]-a[j+1]))
d[i]=d[j]+a[i]−a[j]+max(T,2∗(a[i]−a[j+1]))
所以
O
(
N
2
)
O(N^2)
O(N2)的算法就完成了
那么怎么优化呢
其实主要是那个max的问题,然后又考虑到a[i]-a[j+1]是随着i的增加一直增加的,我们可以用一个单调队列来维护,我们维护一个单增的队列,然后维护队首2*(a[i]-a[j+1])<=T,然后从队首转移,这时候转移式就变成了
d
[
i
]
=
d
[
j
]
+
a
[
i
]
−
a
[
j
]
+
T
d[i]=d[j]+a[i]-a[j]+T
d[i]=d[j]+a[i]−a[j]+T。
而那些已经出队的点呢?这时候转移式就变成了
d
[
i
]
=
d
[
j
]
+
a
[
i
]
−
a
[
j
]
+
2
∗
(
a
[
i
]
−
a
[
j
+
1
]
)
d[i]=d[j]+a[i]-a[j]+2*(a[i]-a[j+1])
d[i]=d[j]+a[i]−a[j]+2∗(a[i]−a[j+1])
或者再化简一下
d
[
i
]
=
3
∗
a
[
i
]
+
d
[
j
]
−
a
[
j
]
−
2
∗
a
[
j
+
1
]
)
d[i]=3*a[i]+d[j]-a[j]-2*a[j+1])
d[i]=3∗a[i]+d[j]−a[j]−2∗a[j+1])
所以对于所有出队的节点,我们储存一个最小的
d
[
j
]
−
a
[
j
]
−
2
∗
a
[
j
+
1
]
d[j]-a[j]-2*a[j+1]
d[j]−a[j]−2∗a[j+1],然后转移即可
最后的d[n]并不是最终答案,我们还要加上m-a[n]
考场上为了保险写的分段…
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100005;
typedef long long ll;
const ll INF=1e15;
ll d[N];
int n,m,T;
ll a[N];
int q[N];
int s,t;
int main()
{
//freopen("computer.in","r",stdin);
//freopen("computer.out","w",stdout);
scanf("%d%d%d",&n,&m,&T);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
d[i]=INF;
}
if(n<=1000){
for(int i=1;i<=n;i++)
for(int j=0;j<i;j++){
ll f=max(a[i]-a[j+1],T-(a[i]-a[j+1]));
d[i]=min(d[i],d[j]+a[i]-a[j]+f+max(a[i]-a[j+1],T-f));
}
ll ans=d[n]+m-a[n];
printf("%lld\n",ans);
}
else{
s=1,t=1;
ll z=INF;
for(int i=1;i<=n;i++){
while(s<=t&&2*(a[i]-a[q[s]+1])>T){
z=min(z,d[q[s]]-a[q[s]]-2*a[q[s]+1]);
s++;
}
d[i]=min(d[i],a[i]+z+2*a[i]);
//d[i]=min(d[i],d[q[s]]+a[i]-a[q[s]]+2*(a[i]-a[q[s]+1]));
d[i]=min(d[i],d[q[s]]+a[i]-a[q[s]]+T);
while(s<=t&&d[q[s]]-a[q[s]]>=d[i]-a[i])
t--;
q[++t]=i;
}
ll ans=d[n]+m-a[n];
printf("%lld\n",ans);
}
}