向紫题出发!
最短路果然是水题法宝
思路
自底向上分析问题。
拆一个点分两步:1.拆结界发生器。2.去这个点。
但是,机器人是可以分成几路走的。
所以这两个过程可以同时发生,只不过快的过程要等慢的过程。
所以,若拆第 i 个点的结界发生器需要 in[i],去这个点需要 arr[i],
则拆第 i 个点需要max(in[i], arr[i])
。(要等慢的过程结束)
现在问题转化为求 in[i] 和 arr[i]。
对于arr[i],不难想到用最短路算法(dijkstra)求解,但要注意,
dij 的 relax 操作中 $arr[v]=min(arr[v],arr[u]+edge\_len(u,v))$
需要改成 $arr[v]=min(arr[v],dis[u]+edge\_len(u,v))$
($dis[i]$ 表示拆 i 点需要的时间)
为什么呢?只有拆了 u 点才能从 $u\rightarrow v$ 上走过去。
而且入队也没那么容易了。
首先要保证 v 点目前没有结界保护,才能入队。
而且入队的同时还要 $dis[v]=arr[v]$ (都没有结界了直接过去就行)
对于in[i],可以用拓扑排序求解。
但拓扑遍历 u 的后继 v 时,要更新 $in[v]=max(in[v],dis[u])$
(与上面的$max(in[i], arr[i])$同理,快的等慢的,u 保护 v,要先拆 u)
而且如果入度为 0,这个点也可以拆了,入队。
至此,整道题就分析完了。
代码
#include <iostream>
#include <queue>
#include <utility>
#include <functional>
using namespace std;
struct Edge1
{
int v, w, nxt;
}edge1[70001];
int head1[3001], cnt1;
void add1(int u, int v, int w)
{
++cnt1;
edge1[cnt1].v = v;
edge1[cnt1].w = w;
edge1[cnt1].nxt = head1[u];
head1[u] = cnt1;
}
struct Edge2
{
int v, nxt;
}edge2[70001];
int head2[3001], cnt2;
void add2(int u, int v)
{
++cnt2;
edge2[cnt2].v = v;
edge2[cnt2].nxt = head2[u];
head2[u] = cnt2;
}
int n, m, u, v, w, dis[3001], vis[3001], rd[3001], arr[3001], in[3001], l, x;
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > q;
int main()
{
cin >> n >> m;
for(int i = 0;i < m;++i)
cin >> u >> v >> w, add1(u, v, w);
for(int i = 1;i <= n;++i)
{
cin >> l;rd[i] = l;
for(int j = 0;j < l;++j)
cin >> x, add2(x, i);
}
for(int i = 1;i <= n;++i)
dis[i] = arr[i] = 1e9;
dis[1] = arr[1] = 0;q.push(make_pair(0, 1));
while(!q.empty())
{
int u = q.top().second;q.pop();
if(vis[u]) continue;
vis[u] = 1;
for(int i = head1[u];i;i = edge1[i].nxt)
if(arr[edge1[i].v] > dis[u] + edge1[i].w)
{
arr[edge1[i].v] = dis[u] + edge1[i].w;
if(!rd[edge1[i].v])
{
dis[edge1[i].v] = arr[edge1[i].v];
q.push(make_pair(dis[edge1[i].v], edge1[i].v));
}
}
for(int i = head2[u];i;i = edge2[i].nxt)
{
--rd[edge2[i].v];
in[edge2[i].v] = max(in[edge2[i].v], dis[u]);
if(!rd[edge2[i].v])
{
dis[edge2[i].v] = max(in[edge2[i].v], arr[edge2[i].v]);
q.push(make_pair(dis[edge2[i].v], edge2[i].v));
}
}
}
cout << dis[n];
return 0;
}