CCPC2019网络赛 HDU6705.path(图论、贪心、第k短路)

  • 题目:path

  • 题意:给出一张有向图,问所有路径中(一条边可以重复走),第k短的路径长度。
  • 题解:既然要求第k短路,那么必然需要求出最短路、次短路、第3短路、第四短路...那么我们可以用贪心的思维尽可能将边权小的边放入队列中,每次取出边权最小的边,然后此时我们希望再次放入尽可能的小的边进入队列中,则有两种操作方法(我们假设取出的边是pre -> v,并且此时为第cnt短路):
    1. v是pre邻边中第idx小的结点,判断pre是否还有(idx + 1)小的边v'将其压入队列中,那么此时路径总长度为:(第cnt短路的距离)res[cnt] - (pre -> v的距离)g[pre][idx].w + (pre -> v'的距离)g[pre][idx + 1].w。
    2. 若v有邻边,则将其最小的邻边(邻接顶点为x)压入队列中,那么此时路径总长度为:res[cnt] + (v -> x的距离)g[v][0].w。
  • 代码:
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 5e4 + 5;
int T, n, m, Q;
int k[N];
ll res[N];
struct node
{
    int idx, to; //idx:顶点pre到其所有相邻结点边权从小到大的排名(边权越小,排名越高)
    int pre; //父节点(前驱结点) pre -> to
    ll w;
    friend bool operator < (node a, node b)
    {
        return a.w > b.w;
    }
};
struct edge
{
    int to; //相邻的顶点
    ll w; //边权
    friend bool operator < (edge a, edge b)
    {
        return a.w < b.w;
    }
};
vector<edge> g[N];
priority_queue<node> q;

void solve(int maxK)
{
    for(int i = 1; i <= n; i++) //将每个顶点边权最小的边压入队列
    {
        sort(g[i].begin(), g[i].end()); //将每个顶点到达的边从小到大排序
        if(g[i].size()) //判断第i个顶点是否有相邻顶点
            q.push({0, g[i][0].to, i, g[i][0].w});
    }
    int cnt = 0; //第cnt短边
    while(q.size())
    {
        res[++cnt] = q.top().w;
        int idx = q.top().idx, v = q.top().to, pre = q.top().pre, w = q.top().w;
        q.pop();
        if(cnt >= maxK) return; //已经记录完maxK短路的值
        if(idx + 1 < g[pre].size()) //u的父节点还能拓展出比u等级更低(边权更大)的结点
        {
            q.push({idx + 1, g[pre][idx + 1].to, pre, w - g[pre][idx].w + g[pre][idx + 1].w});
        }
        if(g[v].size()) //u拓展出边权最小的结点v可以拓展出其他结点
        {
            q.push({0, g[v][0].to, v, w + g[v][0].w});
        }
    }
}

int main()
{
    scanf("%d", &T);
    while(T --)
    {
        scanf("%d%d%d", &n, &m, &Q);
        for(int i = 1; i <= n; i++) g[i].clear();
        while(q.size()) q.pop();
        for(int i = 1; i <= m; i++)
        {
            int u, v;
            ll w;
            scanf("%d%d%lld", &u, &v, &w);
            g[u].push_back({v, w}); //有向图
        }
        int maxK = 0; //询问中最大的k值
        for(int i = 1; i <= Q; i++)
        {
            scanf("%d", &k[i]);
            maxK = max(maxK, k[i]);
        }
        solve(maxK);
        for(int i = 1; i <= Q; i++)
            printf("%lld\n", res[k[i]]);
    }
    return 0;
}

posted @ 2021-07-18 09:14  ~K2MnO4  阅读(91)  评论(0)    收藏  举报