CF-Round #637-div2-E题(01bfs)
E. Nastya and Unexpected Guest
这道题是01bfs.
题目大意:
丹尼尔想尽快从自己家出发到达爱慕的女孩子家中,假设丹尼尔的家在坐标为0的点,女孩子的家在坐标为n的点上。她们家之间的距离看成一条线段。这条路上有红绿灯,当红灯亮起的时候丹尼尔必须处于安全岛的位置(因为这样才是安全的)
在安全岛上面非常安全,可以改变移动的方向。
在任何绿灯时刻,丹尼尔必须移动(因为去见爱慕女孩子的心情是急切的)
丹尼尔每秒钟移动一个单位。
给出一条路上安全岛的位置,还有红灯的秒数和绿灯的秒数。
规定丹尼尔从家中出发(0点开始)时开始计时,首先是绿灯执行。
绿灯执行完之后换红灯,再换绿灯。如此循环。
问至少需要经过多少时间丹尼尔才能到达女孩子的家中(n点)
如果不能到达输出-1;
最少需要多少时间,实际上是让我们求至少走最多少的路才能到达,也就是求解最短路问题。
题目中的输入安全岛的位置不一定是从小到大输入的,所以我们输入部分处理的时候需要sort.
在sort之前。我们把两个小朋友家的位置也设置为安全岛。也就是坐标为0的点和坐标为n的点加进去。
所以我们的m(安全岛数目)需要加2
安全岛存储在safe[]数组中。
我们用dist[][]数组:
dist[i][j]表示:在第i个安全岛的位置花费j个单位的绿灯(也就是现在的绿灯显示是j秒)时红绿灯一共轮回了多少个轮回。(i从0开始)
我们用vis[][]数组相应的表示是否被访问过:
如果被访问过,我们就不需要再访问了,因为再次访问的时候已经是循环轮回了。如果不标记可能意味着陷入了一个死的轮回,见到爱慕女孩子无望了。
我们再进行状态的转移。到达某一个安全岛的位置只有两种可能的状态转移过来的。第一种是从前一个安全岛位置转移过来的,第二种是从后一个安全岛位置转移过来的。
这样就可以构成bfs的过程了。bfs的过程是:把红绿灯的一个轮回的所有可以走的位置考虑完全。再进行下一个红绿灯轮回。
所以我们bfs采用的是deque(双端队列)
考虑四种情况:
t1代表第几个安全岛的位置(t1从0开始)
t2代表现在绿灯花费了几个单位(也就是到达第t1个安全岛时此轮回过了几个单位时间)
case1: 如果t2为0,代表此红绿灯轮回我们刚刚开始,计算当前安全岛的位置距离终点还有多远,如果可以在此轮回到达我们就可以维护最小的时间秒数。
res = (r + g) * dist[t1][t2] + to;
to代表此安全岛的位置到达终点需要花费的秒数,res算出来的是总花费时间。上面说了dist[t1][t2]记录的是到达第t1个安全岛花费了几个红绿灯轮回。
case2: 如果t2为g(g是绿灯的时间)。代表此轮回中的绿灯时间正好结束。看看下一个状态vis[t1][0]是否被访问过。如果没访问过我们进行:
dist[t1][0] = dist[t1][t2] + 1;此操作。
代表在第t1个安全岛上面等红灯。进入到下一个红绿灯轮回。并且把此状态加入到队列的末尾。并把vis[][]标记。
加入末尾的原因是我们需要把当前红绿灯轮回可能到达的位置全部处理一遍,才进入下一个状态。此case处理完后直接continue即可。
case3: t1不为0时,我们转移到前一个安全岛去,判断当前使用的绿灯时间+转移到前一个安全岛使用的时间是否小于g,并且判断此状态是否被处理过。如果都符合的话就把此状态加入到队头即可。
case4: t1不为m - 1时(因为我们存储是从0下标开始的),我们转移到后面一个安全岛去,判断当前使用的绿灯时间+转移到后一个安全岛使用的时间是否小于g,并且判断此状态是否被处理过。如果都符合的话就把此状态加入到队头即可。
最后答案即可。
代码部分:
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int n, m;
int main()
{
scanf ("%d%d", &n, &m);
vector<int> safe(m + 2);
for (int i = 0; i < m; i++)
{
scanf ("%d", &safe[i + 1]);
}
m += 2;
safe[m - 1] = n;
sort(safe.begin(), safe.end());
ll g, r;
cin >> g >> r;
vector<vector<int> > dist(m, vector<int>(g + 1));
vector<vector<int> > vis(m, vector<int>(g + 1));
deque<pair<int, int> > bfs;
bfs.push_back(make_pair(0, 0));
vis[0][0] = 1;
ll ans = -1;
while (bfs.size())
{
int t1 = bfs.front().first;
int t2 = bfs.front().second;
bfs.pop_front();
if (!t2)
{
int to = n - safe[t1];//当前位置还差多少步到达终点
if (to <= g)// 步数所花时间小于绿灯时间
{
ll res = (r + g) * dist[t1][t2] + to;
if (ans == -1 || ans > res)
{
ans = res;
}
}
}
if (t2 == g)
{
if (!vis[t1][0])
{
dist[t1][0] = dist[t1][t2] + 1;
bfs.push_back(make_pair(t1, 0));
vis[t1][0] = 1;
}
continue;
}
if (t1)
{
int to = t2 + safe[t1] - safe[t1 - 1];
if (to <= g && !vis[t1 - 1][to])
{
vis[t1 - 1][to] = 1;
dist[t1 - 1][to] = dist[t1][t2];
bfs.push_front(make_pair(t1 - 1, to));
}
}
if (t1 < m - 1)
{
int to = t2 + safe[t1 + 1] - safe[t1];
if (to <= g && !vis[t1 + 1][to])
{
vis[t1 + 1][to] = 1;
dist[t1 + 1][to] = dist[t1][t2];
bfs.push_front(make_pair(t1 + 1, to));
}
}
}
cout << ans << endl;
return 0;
}