向紫题出发!

最短路果然是水题法宝

就是这题大家快来切了他

思路

自底向上分析问题。

拆一个点分两步: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;
}
posted @ 2021-07-21 16:18  5k_sync_closer  阅读(26)  评论(2编辑  收藏  举报  来源