Description
给定数轴上的 个关键点 ,保证其中包含 。需要从 走到 从 时刻开始,在接下来的 个单位时间内必须不停移动,每个单位时间可以移动一个单位距离。如果你现在在两个关键点之间的线段上,你就只能按照上一步行走的 方向继续移动。如果你现在在任意一个关键点上,那么你下一步可以自由选择往左或往右走。当然,你不能移动到 0 的左 边或是 的右边。
个单位时间结束后,你必须恰好在任意一个关键点上, 等待 个单位时间再继续移动。接下来, 在 个单位时间结束后,重新开始 个可以继续移动的单位时间,依此循环。 求按照上述规则,从 走到 的最短时间。如果无法完成,输出 。
Solution
将 从小到大排序,把 视为一个周期。答案是若干个周期与若干个单位时间之和。在答案的不同周期的同一时间,你都不会在一个相同的点上,否则会构成一个多余的循环。
令 走到 点,当前周期的绿灯开始了 个单位时间,所需的最小周期数。因为绿灯不能驻足,红灯需在安全点上,所以答案中可能从 走到 再走回来。所以要转移相邻的两点。转移考虑的问题有
- 转移合法,要在绿灯结束前停在关键点上。
- 走到关键点上,绿灯有没有正好结束,如果结束了那么新增一个周期。
边权只有 和 ,可以 01 BFS。时间复杂度 。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mp make_pair
#define pf push_front
#define pb push_back
#define F first
#define S second
const int N = 1e4 + 5, INF = 0x3f3f3f3f;
inline int read() {
int x = 0, f = 0; char ch = 0;
while (!isdigit(ch)) f |= ch == '-', ch = getchar();
while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return f ? -x : x;
}
int n, m, g, r, d[N], dis[N][1000 + 5];
deque <pair <int, int> >q;
void update(int x, int t, int nx, int nt) {
if (nt > g) return ;
int _nt = (nt == g ? 0 : nt);
if (dis[nx][_nt] == -1) {
dis[nx][_nt] = dis[x][t] + (nt == g);
if (nt < g) q.pf(mp(nx, _nt));
else q.pb(mp(nx, _nt));
}
}
signed main() {
n = read(), m = read();
for (int i = 1; i <= m; i++) d[i] = read();
g = read(), r = read();
sort(d + 1, d + m + 1);
memset(dis, -1, sizeof(dis));
dis[1][0] = 0; q.pf(mp(1, 0));
while (!q.empty()) {
int x = q.front().F, t = q.front().S;
q.pop_front();
if (x > 1) update(x, t, x - 1, t + d[x] - d[x - 1]);
if (x < m) update(x, t, x + 1, t + d[x + 1] - d[x]);
}
ll ans = -1;
for (int i = 0; i < g; i++)
if (dis[m][i] !=- 1) {
ll t = 1ll * dis[m][i] * (g + r) + i - (!i && dis[m][i]) * r;
if (ans == -1 || t < ans) ans = t;
}
printf("%lld\n", ans);
return 0;
}