cf947div3E

https://codeforces.com/contest/2014/problem/E

题意:n个点,m条边,h匹马,第二行给出h匹马在哪些节点,第3-m+2行给出边和走这条边的时间.使用马匹可以将走某一条边的时间缩短一半,现在有两个人,一个在节点1,一个在节点n-1,问最短相遇时间,考虑两边跑dijkstra算法.

horse[i]表示这个节点是否有马,array[0]是与它连通的节点,array[1]是走这条边所用的时间.

void solve() {
    int n, m, h;//n个节点,m条边,h匹马
    std::cin >> n >> m >> h;
    std::vector<bool>horse(n);
    for (int i = 0; i < h; i++) {//输入马匹位置,并用horse记录
        int a;
        std::cin >> a;
        a--;
        horse[a] = true;
    }
    std::vector<std::vector<std::array<int, 2>>>adj(n);//表示与他连通的下一个节点和边的值
    for (int i = 0; i < m; i++) {
        int u, v, w;
        std::cin >> u >> v >> w;
        u--;
        v--;
        adj[u].push_back({ v,w });
        adj[v].push_back({ u,w });
    }
    
}

下面是dijkstra算法

void solve() {
    int n, m, h;//n个节点,m条边,h匹马
    std::cin >> n >> m >> h;
    std::vector<bool>horse(n);
    for (int i = 0; i < h; i++) {//输入马匹位置,并用horse记录
        int a;
        std::cin >> a;
        a--;
        horse[a] = true;
    }
    std::vector<std::vector<std::array<int, 2>>>adj(n);//表示与他连通的下一个节点和边的值
    for (int i = 0; i < m; i++) {
        int u, v, w;
        std::cin >> u >> v >> w;
        u--;
        v--;
        adj[u].push_back({ v,w });
        adj[v].push_back({ u,w });
    }
    //节点u=2*x+t,其中x表示原节点,t=0表示步行状态,t=1表示骑马状态
    auto dijkstra = [&](int s) {//s是起始位置
        std::vector<ll>dis(2 * n, inf);//
        //第一个是距离d,表示该s到该节点的最小距离,第二个是状态u
        std::priority_queue<std::pair<ll, int>, std::vector<std::pair<ll, int>>, std::greater<>>pq;
        pq.emplace(0LL, 2 * s);//先给一个起始点的步行状态,它和自己的距离为0
        while (!pq.empty()) {
            auto tt = pq.top();//取出首元素
            ll d = tt.first;//拿出第一个元素
            int u = tt.second;//拿出第二个元素
            pq.pop();
            if (dis[u] != inf)continue;//如果该距离已经计算过,就continue
            dis[u] = d;//第一个点到自己的距离就是0
            int x = u / 2;//取出节点和状态
            int t = u % 2;
            if (!t && horse[x]) {//t为0且该点有马
                pq.emplace(d, 2 * x + 1);//将距离和骑马的状态放入优先队列
            }
            //骑上马以后显然是可以一直骑着的,
            for (auto dd : adj[x]) {//拿出它的子节点
                int y = dd[0];//节点
                int w = dd[1];//距离
                pq.emplace(d + (t ? w / 2 : w), 2 * y + t);//如果t骑马,那么距离要除以2,如果没骑马,那么距离为w
                //状态继承自前一个节点
            }
        }
        std::vector<ll>d(n, inf);
        for (int i = 0; i < n; i++) {
            d[i] = std::min(dis[2 * i], dis[2 * i + 1]);//骑马还是不骑马走的距离更短
        }
        return d;
    };
    
}

只有一个参数,表示源节点,也就是从哪开始,用dis[i]表示,源节点到该节点的最短用时,优先队列里存距离d和状态u,d表示到该节点的用时,u是由x和t复合而成的(u有2*n个,所以dis也是2*n个),u=2*x+t,x表示节点,t表示当前状态是否使用了马匹,0表示步行,1表示马匹.首先从起始点s开始,第一个状态肯定是不使用马匹的状态,即便节点1有马匹,那也是在第一个状态之后的第二个状态.按最短时间拿出节点,首先判断这个节点的dis是否已经有值,如果有值,就直接continue,因为优先队列是按时间从小到大排序的,后取出来不可能比当前dis[u]的值大,所以直接continue,紧接着分解节点状态成x和t,如果t是没有马匹的状态0,且horse[x]=1,即该点有马匹,那就将这个后继状态放入到队列当中,紧接着遍历x的子节点,将后继状态放入到队列当中,t?w/2:w这里的判断表示如果有马,那么用时得缩短一半,2*y+t,这里的t直接继承自上一个节点,不需要额外去处理是否有马匹,因为没有马匹到马匹的状态转换会在上面进行,使用d数组,表示走到该节点的最短用时,直接对有马匹和没有马匹的状态取min

void solve() {
    int n, m, h;//n个节点,m条边,h匹马
    std::cin >> n >> m >> h;
    std::vector<bool>horse(n);
    for (int i = 0; i < h; i++) {//输入马匹位置,并用horse记录
        int a;
        std::cin >> a;
        a--;
        horse[a] = true;
    }
    std::vector<std::vector<std::array<int, 2>>>adj(n);//表示与他连通的下一个节点和边的值
    for (int i = 0; i < m; i++) {
        int u, v, w;
        std::cin >> u >> v >> w;
        u--;
        v--;
        adj[u].push_back({ v,w });
        adj[v].push_back({ u,w });
    }
    //节点u=2*x+t,其中x表示原节点,t=0表示步行状态,t=1表示骑马状态
    auto dijkstra = [&](int s) {//s是起始位置
        std::vector<ll>dis(2 * n, inf);//
        //第一个是距离d,表示该s到该节点的最小距离,第二个是状态u
        std::priority_queue<std::pair<ll, int>, std::vector<std::pair<ll, int>>, std::greater<>>pq;
        pq.emplace(0LL, 2 * s);//先给一个起始点的步行状态,它和自己的距离为0
        while (!pq.empty()) {
            auto tt = pq.top();//取出首元素
            ll d = tt.first;//拿出第一个元素
            int u = tt.second;//拿出第二个元素
            pq.pop();
            if (dis[u] != inf)continue;//如果该距离已经计算过,就continue
            dis[u] = d;//第一个点到自己的距离就是0
            int x = u / 2;//取出节点和状态
            int t = u % 2;
            if (!t && horse[x]) {//t为0且该点有马
                pq.emplace(d, 2 * x + 1);//将距离和骑马的状态放入优先队列
            }
            //骑上马以后显然是可以一直骑着的,
            for (auto dd : adj[x]) {//拿出它的子节点
                int y = dd[0];//节点
                int w = dd[1];//距离
                pq.emplace(d + (t ? w / 2 : w), 2 * y + t);//如果t骑马,那么距离要除以2,如果没骑马,那么距离为w
                //状态继承自前一个节点
            }
        }
        std::vector<ll>d(n, inf);
        for (int i = 0; i < n; i++) {
            d[i] = std::min(dis[2 * i], dis[2 * i + 1]);//骑马还是不骑马走的距离更短
        }
        return d;
    };
    auto dl = dijkstra(0);
    auto dn = dijkstra(n - 1);
    ll ans = inf;
    for (int i = 0; i < n; i++) {
        ans = std::min(ans, std::max(dl[i], dn[i]));//两个人都到达这个点,那么时间肯定是到达这慢的那一个,然后取min
    }
    if (ans == inf) { ans = -1; }//如果ans还是inf,那说明对于每一个点,他们都不可能在这个点相遇,输出-1
    std::cout << ans << "\n";
}

最后对0和n-1两个起始节点跑一遍dijkstra,遍历每一个节点,假设他们在1这个节点相遇,那么用时显然是更大的那个,所以是对max(dl[i],dn[i])取min,如果最后ans没找到答案,说明对于每一个点,他们都不可能相遇,将其设为-1,最后输出ans即可.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值